Feign源码解析之代理类的处理逻辑
前篇
- Feign源码解析之注入IOC容器
- Feign源码解析之生成jdk动态代理
正文
上面两篇文章分析了在springboot中feign是如何注入IOC容器并生成jdk动态代理类的,下面我们将着重分析一下feign的代理类的处理逻辑,看一看代理类是如何将接口里的方法转化成http请求的。
之前的文章有提到,在通过FeignClientFactoryBean的getObject方法生成代理类的过程中是根据applicationContext和配置文件获取到其中的属性值的,因此我们可以通过将特定类注入IOC容器或者修改配置的方式实现自定义的实现方式。所以,在文章开始之前首先声明,下文中的分析全部基于springboot中的feign的默认实现。
一. Feign的配置类
首先,feign在springboot中主要有两个配置类,其中的自动配置类FeignAutoConfiguration在之前的文章中已经有提到,下面我们了解一下另一个配置类FeignClientsConfiguration,这个类是在FeignContext的父类方法createContext中的context.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);
语句注入各个client所属的applicationContext中的。
@Configuration
public class FeignClientsConfiguration {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Autowired(required = false)
private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
@Autowired(required = false)
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
@Autowired(required = false)
private Logger logger;
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
@Bean
@ConditionalOnMissingBean
public Encoder feignEncoder() {
return new SpringEncoder(this.messageConverters);
}
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
@Bean
public FormattingConversionService feignConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
feignFormatterRegistrar.registerFormatters(conversionService);
}
return conversionService;
}
@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
@Bean
@ConditionalOnMissingBean(FeignLoggerFactory.class)
public FeignLoggerFactory feignLoggerFactory() {
return new DefaultFeignLoggerFactory(logger);
}
}
FeignClientsConfiguratiob中注入的几个类都是在生成并使用代理类过程中的属性的默认值。
二. FeignInvocationHandler类的invoke方法
接着我们继续看动态类的执行方法:FeignInvocationHandler类的invoke方法,可以发现其执行逻辑为从dispatch中根据method获取到对应的methodHandle以执行invoke方法。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object
otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
由于dispatch属性是由ReflectiveFeign类调用FeignInvocationHandle的构造方法时传入的,我们需要进入ReflectiveFeign的newInstance方法关注dispatch的值。
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if(Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
局部变量methodToHandler即传入FeignInvocationHandle的dispatch属性,遍历feignClient接口的方法进行组装,分3中情况:
- 从object类继承而来的方法,忽略。这一点也和FeignInvocationHandle的invoke方法针对equal、hashcode、toString方法的处理吻合。
- 接口中的default方法,对应的methodHandle的逻辑依旧还是原方法的逻辑
- 其它方法,也是我们最关注的需要进行http请求处理的方法,根据方法的key值从nameToHandler中获取。
可以先了解一下方法的key值是怎么生成的:方法并不复杂,由3部分组成:接口名、方法名、每个方法参数的类型,从而保证了唯一性。
public static String configKey(Class targetType, Method method) {
StringBuilder builder = new StringBuilder();
builder.append(targetType.getSimpleName());
builder.append('#').append(method.getName()).append('(');
for (Type param : method.getGenericParameterTypes()) {
param = Types.resolve(targetType, targetType, param);
builder.append(Types.getRawType(param).getSimpleName()).append(',');
}
if (method.getParameterTypes().length > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.append(')').toString();
}
三. Feign方法到MethodMetaData
接着看apply方法是如何生成nameToHandles的,在apply方法里又调用了contract的parseAndValidatateMetadata方法,在springboot项目中这儿contract默认的contract是SpringMvcContract类,parseAndValidatateMetadata在其父类BaseContract中。
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
checkState( targetType.getTypeParameters().length == 0,
"Parameterized types unsupported: %s",
targetType.getSimpleName());
checkState( targetType.getInterfaces().length <= 1,
"Only single inheritance supported: %s",
targetType.getSimpleName());
if (targetType.getInterfaces().length == 1) {
checkState(targetType.getInterfaces()[0].getInterfaces().length == 0,
"Only single-level inheritance supported: %s",
targetType.getSimpleName());
}
Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
for (Method method : targetType.getMethods()) {
if (method.getDeclaringClass() == Object.class ||
(method.getModifiers() & Modifier.STATIC) != 0 ||
Util.isDefault(method)) {
continue;
}
MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
checkState(!result.containsKey(metadata.configKey()),
"Overrides unsupported: %s",
metadata.configKey());
result.put(metadata.configKey(), metadata);
}
return new ArrayList<MethodMetadata>(result.values());
}
可以看出,该方法的,目的是对接口内的每个方法进行解析,并封装成MethodMetadata,放入容器list内返回。该方法先进行了前置校验,对接口进行校验,要求:
- 接口不是泛型接口,不含有类型参数。
- 接口最多只有1个父类接口,且其父类接口不能再含有父类接口。
接着遍历方法,依次获取并封装每个方法的MethodMetadata,并做一次后置校验,防止重复。同样的,从object类继承的方法、static方法和default方法不处理。
进入同名方法parseAndValidateMetadata,注意SpringMvcContract对该方法进行了重写,当然SpringMvcContract重写后的方法还是先调用了MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
以先执行BaseContract中的对应方法,先看BaseContract的parseAndValidateMetadata方法。
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
MethodMetadata data = new MethodMetadata();
//1.returnType和configKey的属性设置
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
data.configKey(Feign.configKey(targetType, method));
//2.接口注解的解析
if(targetType.getInterfaces().length == 1) {
processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
processAnnotationOnClass(data, targetType);
//3.方法注解的解析
for (Annotation methodAnnotation : method.getAnnotations()) {
processAnnotationOnMethod(data, methodAnnotation, method);
}
checkState(data.template().method() != null,
"Method %s not annotated with HTTP method type (ex. GET, POST)",
method.getName());
Class<?>[] parameterTypes = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
//4.参数和参数注解的解析
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
int count = parameterAnnotations.length;
for (int i = 0; i < count; i++) {
boolean isHttpAnnotation = false;
if (parameterAnnotations[i] != null) {
isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
}
if (parameterTypes[i] == URI.class) {
data.urlIndex(i);
} else if (!isHttpAnnotation) {
checkState(data.formParams().isEmpty(),
"Body parameters cannot be used with form parameters.");
checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method);
data.bodyIndex(i);
data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
}
}
if (data.headerMapIndex() != null) {
checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]);
}
if (data.queryMapIndex() != null) {
checkMapString("QueryMap", parameterTypes[data.queryMapIndex()], genericParameterTypes[data.queryMapIndex()]);
}
return data;
}
这个方法里我们开始了MethodMetadata的初始化,并初步开始进行封装,一共可以分为4步,我们逐一解析。
1. returnType和configKey的属性设置
这两个属性比较简单,从method中获取即可。
2. 接口注解的解析
如果接口上有RequestMapping注解,且value属性值不为空,取其value值作为RequestTemplate中url的前缀,这一点和我们的使用习惯也是吻合的,应该比较好理解。
@Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
if (clz.getInterfaces().length == 0) {
RequestMapping classAnnotation = findMergedAnnotation(clz,
RequestMapping.class);
if (classAnnotation != null) {
// Prepend path from class annotation if specified
if (classAnnotation.value().length > 0) {
String pathValue = emptyToNull(classAnnotation.value()[0]);
pathValue = resolve(pathValue);
if (!pathValue.startsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().insert(0, pathValue);
}
}
}
}
3. 方法注解的解析
方法上需要进行解析的依旧还是RequestMapping注解。 在这一部里,我们对RequestMapping进行解析得到method、path、produces、consumes、headers属性。
@Override
protected void processAnnotationOnMethod(MethodMetadata data,
Annotation methodAnnotation, Method method) {
if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
.annotationType().isAnnotationPresent(RequestMapping.class)) {
return;
}
RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
// HTTP Method
RequestMethod[] methods = methodMapping.method();
if (methods.length == 0) {
methods = new RequestMethod[] { RequestMethod.GET };
}
checkOne(method, methods, "method");
data.template().method(methods[0].name());
// path
checkAtMostOne(method, methodMapping.value(), "value");
if (methodMapping.value().length > 0) {
String pathValue = emptyToNull(methodMapping.value()[0]);
if (pathValue != null) {
pathValue = resolve(pathValue);
// Append path from @RequestMapping if value is present on method
if (!pathValue.startsWith("/")
&& !data.template().toString().endsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().append(pathValue);
}
}
// produces
parseProduces(data, method, methodMapping);
// consumes
parseConsumes(data, method, methodMapping);
// headers
parseHeaders(data, method, methodMapping);
data.indexToExpander(new LinkedHashMap<Integer, Param.Expander>());
}
3.1 method
对应RequestMapping的method属性,对应RequestMapping的method属性。要求RequestMapping最多只能指定1个method属性,如果没有指定,则默认为GET请求。
3.2 path
对应RequestMapping的value属性,对应RequestMapping的url属性。同样要求RequestMapping最多只能指定1个value属性。
3.3 produces
对应RequestMapping的produces属性,对应RequestTemplate的headers的accept属性。
private void parseProduces(MethodMetadata md, Method method,
RequestMapping annotation) {
String[] serverProduces = annotation.produces();
String clientAccepts = serverProduces.length == 0 ? null
: emptyToNull(serverProduces[0]);
if (clientAccepts != null) {
md.template().header(ACCEPT, clientAccepts);
}
}
3.4 consumes
对应RequestMapping的consumes属性,对应RequestTemplate的headers的Content-Type属性。
private void parseConsumes(MethodMetadata md, Method method,
RequestMapping annotation) {
String[] serverConsumes = annotation.consumes();
String clientProduces = serverConsumes.length == 0 ? null
: emptyToNull(serverConsumes[0]);
if (clientProduces != null) {
md.template().header(CONTENT_TYPE, clientProduces);
}
}
3.5 headers
对应RequestMapping的headers属性,对应RequestTemplate的headers属性。 注意注释TODO: only supports one header value per key,每个key只能有1个value,因此只支持“key = value”格式的表达式,不支持“key != value”格式的表达式。
private void parseHeaders(MethodMetadata md, Method method,
RequestMapping annotation) {
// TODO: only supports one header value per key
if (annotation.headers() != null && annotation.headers().length > 0) {
for (String header : annotation.headers()) {
int index = header.indexOf('=');
if (!header.contains("!=") && index >= 0) {
md.template().header(resolve(header.substring(0, index)),
resolve(header.substring(index + 1).trim()));
}
}
}
}
3.6 methodMetadata的indexToExpander设置为空的LinkedHashMap。
4.参数和参数注解的解析
接口和方法注解解析得到的数据主要针对methodMetadata中的RequesTtemplate属性,参数和参数注解的解析得到的数据只要针对methodMetadata中的其它属性。
由于每个参数都可以有多个注解,因此method.getParameterAnnotations()得到的是一个二维数组,接下来我们进入循环对每个参数进行遍历解析。
4.1 参数注解解析
进入processAnnotationsOnParameter方法,方法的返回值为布尔值,字面意思是该参数是否有http相关的注解,表示该参数是否经过参数处理器处理。 在分析方法之前,先了解一下方法中用到的几个SpringMvcContract类的成员变量。
processedMethods
method的feign的key值和method组成的映射对,在进入SpringMvcContract的parseAndValidateMetadata方法时通过this.processedMethods.put(Feign.configKey(targetType, method), method);语句进入对应的元素。
annotatedArgumentProcessors
注解类型和AnnotatedParameterProcessor类组成的映射对,用于feign方法参数的处理,在构造方法设值,项目中调用构造方法时传入的是一个empty list,所以此时的annotatedArgumentProcessors通过getDefaultAnnotatedArgumentsProcessors方法获得,对应@pathVariable、@RequestParam、@RequestHeader 3种注解及其对应的处理器
public SpringMvcContract(
List<AnnotatedParameterProcessor> annotatedParameterProcessors,
ConversionService conversionService) {
Assert.notNull(annotatedParameterProcessors,
"Parameter processors can not be null.");
Assert.notNull(conversionService, "ConversionService can not be null.");
List<AnnotatedParameterProcessor> processors;
if (!annotatedParameterProcessors.isEmpty()) {
processors = new ArrayList<>(annotatedParameterProcessors);
}
else {
processors = getDefaultAnnotatedArgumentsProcessors();
}
this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
this.conversionService = conversionService;
this.expander = new ConvertingExpander(conversionService);
}
private List<AnnotatedParameterProcessor> getDefaultAnnotatedArgumentsProcessors() {
List<AnnotatedParameterProcessor> annotatedArgumentResolvers = new ArrayList<>();
annotatedArgumentResolvers.add(new PathVariableParameterProcessor());
annotatedArgumentResolvers.add(new RequestParamParameterProcessor());
annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());
return annotatedArgumentResolvers;
}
conversionService
同样在构造方法内设值,在调用构造方法时传入值,用于类型转换,对应FeignClientsConfiguration类中注入的FormattingConversionService类
expander
同样在构造方法内设值,this.expander = new ConvertingExpander(conversionService);
在了解了上面的几个成员变量之后,下面的代码应该就比较容易理解了,分两步: 4.1.1 根据注解类型查找参数处理器并解析,只有有1个注解解析成功则isHttpAnnotation 为true。参数处理器的处理逻辑并不复杂,不再解析。 4.1.2 isHttpAnnotation 为true,在methodMetadata的indexToExpander加入expander。
@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data,
Annotation[] annotations, int paramIndex) {
boolean isHttpAnnotation = false;
AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(
data, paramIndex);
Method method = this.processedMethods.get(data.configKey());
for (Annotation parameterAnnotation : annotations) {
AnnotatedParameterProcessor processor = this.annotatedArgumentProcessors
.get(parameterAnnotation.annotationType());
if (processor != null) {
Annotation processParameterAnnotation;
// synthesize, handling @AliasFor, while falling back to parameter name on
// missing String #value():
processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(
parameterAnnotation, method, paramIndex);
isHttpAnnotation |= processor.processArgument(
context, processParameterAnnotation, method);
}
}
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null
&& this.conversionService.canConvert(
method.getParameterTypes()[paramIndex], String.class)) {
data.indexToExpander().put(paramIndex, this.expander);
}
return isHttpAnnotation;
}
4.2 参数类型解析
如果参数类型是URI类型,设置methodMetaData的urlIndex属性
4.3 RequestBody 处理
isHttpAnnotation为false,即如果该参数没有经过参数处理器处理,将其按RequestBody 处理,设置bodyIndex。 需要注意的是两个校验条件,body不能喝form同时使用,最多只能用一个body。
5. headerMapIndex 校验
必须为Map格式,key必须为String,这一点其实对应方法中类似于@requestParam Map<String, Object> params的参数
6. queryMapIndex 校验
必须为Map格式,key必须为String,这一点其实对应方法中类似于@requestHeader Map<String, Object> params的参数。
回到SpringMvcContract的parseAndValidateMetadata方法,我们接着分析在执行完MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
后的操作。可以看出当方法的注解RequestMapping没有指定produces、consumes和headers时,采用接口注解RequestMapping上的对应属性。
@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
this.processedMethods.put(Feign.configKey(targetType, method), method);
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
RequestMapping classAnnotation = findMergedAnnotation(targetType,
RequestMapping.class);
if (classAnnotation != null) {
// produces - use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(ACCEPT)) {
parseProduces(md, method, classAnnotation);
}
// consumes -- use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
parseConsumes(md, method, classAnnotation);
}
// headers -- class annotation is inherited to methods, always write these if
// present
parseHeaders(md, method, classAnnotation);
}
return md;
}
至此,Feign方法到MethodMetaData的封装完成,我们回到上一步,ParseHandlersByName类的apply方法。
四. MethodMetaData到SynchronousMethodHandler
在得到一个接口下所有feign方法组成的MethodMetaData的list后,遍历,分成有form数据、body数据和没有form和body数据3种情况继续封装成BuildFormEncodedTemplateFromArgs、BuildEncodedTemplateFromArgs和BuildTemplateByResolvingArg,其两者是第三者的子类,重写了第三者的resolve方法,得到RequestTemplate的方法不同,然后生成SynchronousMethodHandler类。
public Map<String, MethodHandler> apply(Target key) {
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md);
}
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
public MethodHandler create(Target<?> target, MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options, Decoder decoder, ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, decode404);
}
五. SynchronousMethodHandler 的 invoke 方法
从前面的分析可以得到,我们调用feign接口的方法,进入了FeignInvocationHandle的invoke方法,而在FeignInvocationHandle的invoke里,对于除了object继承而来的方法和default方法以外的feign方法,都会进入SynchronousMethodHandler的invoke方法。
根据生成的参数和封装了MethodMetaDatabuildTemplateFromArgs类得到了RequestTemplate,委托executeAndDecode(template)方法进行发送请求的逻辑,retryer.continueOrPropagate(e)控制了重发逻辑。
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
接着看executeAndDecode方法,
Object executeAndDecode(RequestTemplate template) throws Throwable {
//由RequestTemplate到Request
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
//调用client进行发送,得到响应response
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
}
//由Response到方法返回值
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
return decode(response);
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
return decode(response);
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
//确保连接关闭
ensureClosed(response.body());
}
}
}
除去方法中的打印日志和异常捕捉部分,主要逻辑有3步:
1. 由RequestTemplate到Request
在这里,用户可以往IOC容器注入RequestInterceptor类,来对http请求做一些公共的处理。
Request targetRequest(RequestTemplate template) {
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(new RequestTemplate(template));
}
2.调用client进行发送,得到响应response
调用clinet发送请求,LoadBalancerFeignClient可以处理负载均衡
3.由Response到方法返回值
又细分为以下几种情况:
- 返回值类型为Response
- response.body为null,return response。
- response.body的长度为null或长度超出限定长度,return response,shouldClose = false。 否则,将response.body作为留读取,得到新的response返回,确保断开。
- response.status [200, 300)
- 说明正常执行
- 返回值类型为void,即没有返回值,return null。
- 否则,执行decode.decode进行解码并返回。
- decode404 && response.status() == 404 && void.class != metadata.returnType() 执行decode.decode进行解码并返回。
- 执行errorDecoder.decode进行异常处理,抛出异常。
4.确保连接关闭
- 在finally语句处理,确保该语句被执行。
- 对于shouldClose = false的情况,执行ensureClosed方法。
本文由 创作,采用 知识共享署名4.0 国际许可协议进行许可。本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。最后编辑时间为: 2021/07/30 01:57