目录
2.1、feign.ReflectiveFeign#newInstance
2.2、feign.SynchronousMethodHandler
3.1、Feign请求预处理(RequestInterceptor)
3.2、FeignClient请求优先走本地接口实现类(思路)
版本:spring-cloud-openfeign-core:3.1.2
1、FeignClient注册逻辑
1.1、入口@EnableFeignClients
@EnableFeignClients -> @Import(FeignClientsRegistrar.class)
1.2、FeignClientsRegistrar
org.springframework.cloud.openfeign.FeignClientsRegistrar
通过实现ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions方法,可以完成自定义Bean的注入。
1.2.1、registerBeanDefinitions
- registerDefaultConfiguration:注入FeignClient的全局配置,将@EnableFeignClients中的defaultConfiguration属性中配置的class类型注入到容器。
- registerFeignClients:将所有添加了@FeignClient注解的接口,创建Bean并注入到容器中。
1.2.2、registerFeignClients
扫描指定package下所有@FeignClient修饰的接口,注入FeignClient客户端(代理)对象,供应用程序远程调用时使用。
1.2.3、registerFeignClient
根据@FeignClient注解中的属性配置,定义FeignClient对应的BeanDefiniton对象,并注入到容器中。核心是一个FeignClientFactoryBean对象,该对象是一个FactoryBean对象,生成的实例是FactoryBean.getObject()方法返回的对象;
对于FeignClientFactoryBean来说,getObject()返回的就是Feign接口类的代理对象。
1.3、FeignClientFactoryBean
org.springframework.cloud.openfeign.FeignClientFactoryBean
1.3.1、getObject -> getTarget
如果@FeignClient注解配置了url属性,返回一个默认代理类;
反之,则返回一个带有负载均衡功能的代理类(feign + ribbon)。
1.3.2、loadBalance
targeter.target -> org.springframework.cloud.openfeign.DefaultTargeter#target -> feign#target
targeter.target:创建代理对象
1.4、feign.Feign#target
包含build()和newInstance()两个方法。
- build(): FeignClient的构造器,返回一个ReflectiveFeign对象。
- newInstance(): 创建FeignClient的动态代理对象
2、FeignClient接口调用逻辑
2.1、feign.ReflectiveFeign#newInstance
包含方法增强和FeignClient增强。
2.1.1、方法增强
Map<Method, MethodHandler> methodToHandler保存了方法对应的增强逻辑,MethodHandler具体类型为SynchronousMethodHandler;
2.1.2、FeignClient增强
使用动态代理的方式增强Feign请求逻辑,增强逻辑在ReflectiveFeign.FeignInvocationHandler的invoke方法中。当有Feign请求时,会进入该方法。
dispatch即为2.1.1中的methodToHandler方法增强映射,在这里会手动调用SynchronousMethodHandler中的invoke方法。
2.2、feign.SynchronousMethodHandler
2.2.1、invoke
buildTemplateFromArgs.create:创建RequestTemplate对象,封装请求信息。
executeAndDecode:服务实例负载均衡与请求发送。
2.2.2、executeAndDecode
- targetRequest:Request拦截器处理,可通过实现RequestInterceptor接口,进行Feign请求的预处理。
- client.execute为主要方法,以FeignBlockingLoadBalancerClient举例:
loadBalancerClient.choose:获取服务实例
buildRequest:拼接真实请求
executeWithLoadBalancerLifecycleProcessing:执行请求发送并返回响应结果,往下走会走到LoadBalancerUtils的executeWithLoadBalancerLifecycleProcessing方法,该方法的核心为feignClient.execute,底层会利用JDK提供的HttpURLConnection发起远程的HTTP通讯。
3、一些实用功能
3.1、Feign请求预处理(RequestInterceptor)
发送请求前,对Feign请求信息预先进行处理。
自定义Feign拦截器类,继承RequestInterceptor接口,实现apply方法。
比如,要实现对每一个feign请求增加authorization鉴权信息:
// 方式一:
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String authorization = request.getHeader("authorization");
if (StringUtils.hasLength(authorization)) {
requestTemplate.header(authorization, authorization);
}
}
}
// 方式二:
@Bean
@ConditionalOnMissingBean
RequestInterceptor feignRequestInterceptor(){
return template -> {
// 认证信息处理
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(attributes != null){
HttpServletRequest request = attributes.getRequest();
// 添加"authorization"
template.header("authorization", request.getHeader("authorization"));
}
};
}
3.2、FeignClient请求优先走本地接口实现类(思路)
// 记录Feign接口名与其下所有方法的映射关系
private final static Map<String, List<MethodMappingInfo>> FEIGN_METHOD_MAPPING = new HashMap<>();
// 记录Controller方法与MethodBean的映射关系,MethodBean包含Controller的Bean和对应的Method对象
private final static Map<String, MethodBean> METHOD_BEAN = new HashMap<>();
- 重写FeignClientFactoryBean的getObject()方法,在获取代理对象(getTarget())之前,将Feign接口与方法映射信息填充进FEIGN_METHOD_MAPPING;
- 自定义类继承RequestMappingHandlerMapping接口,重写getMappingForMethod方法。拦截所有Controller方法,比较Controller方法和FEIGN_METHOD_MAPPING的方法信息,如果url,入参、出参等信息完全一致,则将当前Controller方法信息填充进METHOD_BEAN中。
- 自定义FeignInvocationHandlerFactory继承InvocationHandlerFactory,实现create方法,当有Feign请求到来时,先判断METHOD_BEAN中是否有对应url的本地Controller方法,如果有,通过反射直接调用本地Controller方法;如果没有,再通过Feign进行远程方法调用。
- 自定义FeignCapability继承CombineCapability,实现enrich方法,创建一个FeignInvocationHandlerFactory对象并返回。
- 将FeignCapability通过@Bean注解,添加到容器中。
PS:第4、5两个步骤,是为了在ReflectiveFeign.newInstance方法中,factory.create(target, methodToHandler)这一步执行到自定义的FeignInvocationHandlerFactory中的create方法,实现在Feign请求到来时,走自定义的动态代理逻辑。
以上内容为个人学习理解,如有问题,欢迎在评论区指出。
部分内容截取自网络,如有侵权,联系作者删除。