OpenFeign核心源码简读

目录

1、FeignClient注册逻辑

1.1、入口@EnableFeignClients

1.2、FeignClientsRegistrar

1.2.1、registerBeanDefinitions

1.2.2、registerFeignClients

1.2.3、registerFeignClient

1.3、FeignClientFactoryBean

1.3.1、getObject -> getTarget

1.3.2、loadBalance

1.4、feign.Feign#target

2、FeignClient接口调用逻辑

2.1、feign.ReflectiveFeign#newInstance

2.1.1、方法增强

2.1.2、FeignClient增强

2.2、feign.SynchronousMethodHandler

2.2.1、invoke

2.2.2、executeAndDecode

3、一些实用功能

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<>();
  1. 重写FeignClientFactoryBean的getObject()方法,在获取代理对象(getTarget())之前,将Feign接口与方法映射信息填充进FEIGN_METHOD_MAPPING;
  2. 自定义类继承RequestMappingHandlerMapping接口,重写getMappingForMethod方法。拦截所有Controller方法,比较Controller方法和FEIGN_METHOD_MAPPING的方法信息,如果url,入参、出参等信息完全一致,则将当前Controller方法信息填充进METHOD_BEAN中。
  3. 自定义FeignInvocationHandlerFactory继承InvocationHandlerFactory,实现create方法,当有Feign请求到来时,先判断METHOD_BEAN中是否有对应url的本地Controller方法,如果有,通过反射直接调用本地Controller方法;如果没有,再通过Feign进行远程方法调用。
  4. 自定义FeignCapability继承CombineCapability,实现enrich方法,创建一个FeignInvocationHandlerFactory对象并返回。
  5. 将FeignCapability通过@Bean注解,添加到容器中。

PS:第4、5两个步骤,是为了在ReflectiveFeign.newInstance方法中,factory.create(target, methodToHandler)这一步执行到自定义的FeignInvocationHandlerFactory中的create方法,实现在Feign请求到来时,走自定义的动态代理逻辑。

以上内容为个人学习理解,如有问题,欢迎在评论区指出。

部分内容截取自网络,如有侵权,联系作者删除。