4、FeignClientFactoryBean创建动态代理
由于FeignClientFactoryBean是tFactoryBean所有获取对象是从getObject中
4.1 获取组件属性
从容器中获取对应的FeignContext,
我们进入feign(contex)
这些方法都是调用的get方法,只是类型不同而已
在getObject⽅法中,⾸先从applicationContext中、也就是从Spring容器中获取了⼀个FeignContext组件,应该是Feign存放⼯具类的⼀个上下⽂组件,然后从FeignContext中获取到了FeignLoggerFactory组件,⼀路追进去发现,原来在底层也是维护了⼀个Spring容器的缓存Map<String, AnnotationConfigApplicationContext>。
Feign在执⾏时涉及到⼀系列的组件,所以Feign⼲脆为每个服务创建了⼀个Spring容器类ApplicationContext,⽤来存放每个服务各⾃所需要的组件,每个服务的这些⼯具类、组件都是互不影,所以我们看到它在底层是通过⼀个Map来缓存的,key为服务名,value为服务所对应的的spring容器ApplicationContext。
接着我们想不管是FeignContext、FeignLoggerFactory还是Encoder、Decoder、Contract,他们都是接⼝,那具体的实现类是什么呢?
因为它们都是直接从Spring的容器中取出来的,这就意味着在某个地⽅事先注⼊到了Spring容器中了,所以我们还得从⼀些地⽅、如配置类⾥⾯看下。
在SpringCloud系列组件中,设计者都⽐较喜欢把⼀些组件的初始化都放在⼀些XxxConfiguration中,然后需要使⽤到的时候通过容器直接取,但是对于我们分析⽽⾔、还是有点麻烦的,毕竟还得去找。
⾸先因为我们当前是在FeignClientFactoryBean中,所以⾸先当然是从它对应路径下开始寻找,看下有没有类似Configuration相关的类,如下图所示:
FeignAutoConfiguration
FeignClientsConfiguration
上面是对应get内容
下面对应configureFeign
我们看方法会发现里面就是先获取java类配置,然后再获取属性配置,属性配置会把代码配置给覆盖掉,所以我们需要属性配置优先级高于代码
4.2 创建动态代理对象
因为@FeignClient默认属性是name,所以url为空,所以第一个if条件成立;而注解中name属性值是msb-stock,所以url最后为http://msb-stock然后进⼊loadBalance⽅法中,从⽅法名称上来看、从这⾥开始要和负载均衡有点关系了。
获取的Client是LoadBalanceFeignClient,对应的Targeter是HystrixTargeter
创建实例
创建动态代理的类。
在newInstance⽅法中,⼀开始先调⽤targetToHandlersByName获取了Map<String, MethodHandler>,因为我们这⾥是动态代理、⽽且代理的是接⼝的⽅法,所以我们基本可以猜测到这⾥是想要为每个⽅法创建⼀个⽅法的处理器,也就是MethodHandler,但是具体怎么创建呢?
我们进入factory.create查看是怎样创建MethodHandler
这里重点就是我们会把我们对应的接口封装为<font
color="red">SynchronousMethodHandler</font>,后面调用的时候我们会进入这个方法
理解了前⾯的内容之后,现在就⽐较好理解了,创建动态代理、怎么代理呢,就是遍历了下⽅法,把之前的Map<String, MethodHandler>、本来以⽅法名称为key的形式、换成了以Method为key的methodToHandler的Map<Method, MethodHandler>。
⽅便通过Method就能从Map获取到对应的Handler,然后创建了InvocationHandler,通过JDK的API 创建⼀个JDK的动态代理并返回。
好,我们查看创建jdk动态代理InvocationHandler
=
到这⾥我们基本上可以知道:⽐如当服务B的getById⽅法被调⽤时、会调⽤服务A的getById⽅法,⽽服务A这⾥因为是通过Feign声明式调⽤的,肯定会先通过服务A的Feign动态代理FeignInvocationHandler,从动态代理中的Map<Method, MethodHandler>中、找到具体某个⽅法的SynchronousMethodHandler,然后执⾏该SynchronousMethodHandler中的invoke⽅法来处理⽅法的逻辑。
并且根据我们最早的猜测,SynchronousMethodHandler中的逻辑⽆⾮就是将前⾯从@FeignClient、接⼝上的@RequestMapping、@PathVariable、@RequestParams等注解中解析到的信息,拼凑成⼀个完整的HTTP请求,然后发起请求并接对应的响应信息。
4.3 进行调用
jdk动态代理的调用,会调用FeignInvocationHandler.invoke
从缓存中通过Method获取对应的MethodHandler (SynchronousMethodHandler)
然后调用SynchronousMethodHandler
这里通过client进行执行,这里面应该有负载均衡和调用
4.3.1 获取负载均衡器
4.3.2通过负载均衡器获取服务
LoadBalancerCommand从名称可以推测这是一个负载均衡器,调用submit里面会调用selectServer方法 复制代码
获取负载均衡器ZoneAwareLoadBalancer,然后根据路由规则进行调用这里就是ribbon的内容了
4.3.3 重构URL
可以进入看一下就是拼接字符串
4.3.4 发送http请求
默认我们会调用最后HttpUrlConnection