文章目录
- 前言
- 正文
- 一、前戏,FeignClientFactoryBean入口方法的分析
- 1.1 从BeanFactory入手
- 1.2 AbstractBeanFactory#doGetBean(...)中对FactoryBean的处理
- 1.3 结论 FactoryBean#getObject()
- 二、FeignClientFactoryBean实现的getObject()
- 2.1 FeignClientFactoryBean#getTarget()
- 2.2 获取`Targeter`实例
- 2.3 ReflectiveFeign#newInstance(...)
- 2.4 生成代理对象
- 三、动态代理原理全流程梳理
- 附录
- 附1:本系列文章链接
前言
本篇是SpringCloud原理系列的 OpenFeign 模块的第三篇。
主要内容是接第二篇,在将FeignClientFactoryBean
的bean描述器注册到容器中后,我们的容器在初始化时,使用了饥饿模式,直接创建Bean。本文就围绕FeignClientFactoryBean
来分析动态代码的应用,以及它本身的初始化过程。
使用java 17,spring cloud 4.0.4,springboot 3.1.4
正文
一、前戏,FeignClientFactoryBean入口方法的分析
首先看看它的类图:
实现了众多接口,我们先记得它实现了 FactoryBean接口。
后文分析时有用到。
1.1 从BeanFactory入手
我们都知道,从Spring 容器中获取一个bean,都会用到BeanFactory的实现类。
在众多继承关系中,AbstractBeanFactory
的存在,帮助实现了很多特殊逻辑。也包括对FactoryBean实现类的处理。
在这个抽象Bean工厂中,实现了getBean的方法。
public Object getBean(String name) throws BeansException {return this.doGetBean(name, (Class)null, (Object[])null, false);
}public <T> T getBean(String name, Class<T> requiredType) throws BeansException {return this.doGetBean(name, requiredType, (Object[])null, false);
}public Object getBean(String name, Object... args) throws BeansException {return this.doGetBean(name, (Class)null, args, false);
}public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args) throws BeansException {return this.doGetBean(name, requiredType, args, false);
}
可以看到,都是直接调用了一个 doGetBean
方法。
1.2 AbstractBeanFactory#doGetBean(…)中对FactoryBean的处理
这个doGetBean
方法太长了,我这里不做粘贴,只挑重点的说。
在这个方法中,获取Bean的时候,有调用 getObjectForBeanInstance(...)
方法。而该方法中就对FactoryBean
做了处理。
处理逻辑如下:
做了判断,如果当前实例是factoryBean的类型,就调用了getCachedObjectForFactoryBean
或 getObjectFromFactoryBean
。这里有优化,从缓存中获取不到时,会从工厂中获取,也就是去创建实例。
1.3 结论 FactoryBean#getObject()
这里的关键方法,doGetObjectFromFactoryBean
就使用了FactoryBean接口。
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {Object object;try {object = factory.getObject();} catch (FactoryBeanNotInitializedException var5) {throw new BeanCurrentlyInCreationException(beanName, var5.toString());} catch (Throwable var6) {throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", var6);}if (object == null) {if (this.isSingletonCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName, "FactoryBean which is currently in creation returned null from getObject");}object = new NullBean();}return object;}
可以看到调用了FactoryBean#getObject()
,并返回了对应的实例。而这也就是本篇的开始。
二、FeignClientFactoryBean实现的getObject()
从类的定义上,FeignClientFactoryBean 实现了FactoryBean。
所以,在从容器中获取该类型实例时,也就会调用到getObject()
。
实现如下:
@Override
public Object getObject() {return getTarget();
}
你没看错,它就一行代码,但是就这一行代码,其复杂程度却丝毫不小。
2.1 FeignClientFactoryBean#getTarget()
源码如下:
<T> T getTarget() {// 从容器获取FeignClientFactory实例FeignClientFactory feignClientFactory = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class): applicationContext.getBean(FeignClientFactory.class);// 获取feign的建造器Feign.Builder builder = feign(feignClientFactory);// 处理url等参数if (!StringUtils.hasText(url) && !isUrlAvailableInConfig(contextId)) {if (LOG.isInfoEnabled()) {LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");}if (!name.startsWith("http")) {url = "http://" + name;}else {url = name;}url += cleanPath();return (T) loadBalance(builder, feignClientFactory, new HardCodedTarget<>(type, name, url));}if (StringUtils.hasText(url) && !url.startsWith("http")) {url = "http://" + url;}// 处理urlString url = this.url + cleanPath();// 通过工厂获取client实例Client client = getOptional(feignClientFactory, Client.class);// 生成clientif (client != null) {if (client instanceof FeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((FeignBlockingLoadBalancerClient) client).getDelegate();}if (client instanceof RetryableFeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();}builder.client(client);}// 应用自定义参数applyBuildCustomizers(feignClientFactory, builder);// 获取Targeter实例Targeter targeter = get(feignClientFactory, Targeter.class);// 返回解析,使用动态代理绑定MethodHandler,最终返回代理对象return targeter.target(this, builder, feignClientFactory, resolveTarget(feignClientFactory, contextId, url));}
以上的核心步骤其实就是以下几步:
- 从容器中获取
FeignClientFactory
实例; - 依据
FeignClientFactory
实例生成Feign.Builder
实例; - 拼接有效的url
- 获取client
- 处理自定义构建参数
- 获取
Targeter
实例 - 动态代理获取代理对象
本文主要关注的是第6、7步。
2.2 获取Targeter
实例
首先,Targeter
有两个实现类:DefaultTargeter
和 FeignCircuitBreakerTargeter
。
默认是从容器中直接获取到DefaultTargeter
。如果使用了断路器,则会获取到 FeignCircuitBreakerTargeter
。
默认实现如下:
@Overridepublic <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,Target.HardCodedTarget<T> target) {return feign.target(target);}
其中 Feign.Builder#target(...)
如下:
@Override
public <T> T target(Target<T> target) {return this.build().newInstance(target);
}
而这里的build()
方法,则会生成一个ReflectiveFeign
实例。
使用创建的ReflectiveFeign
实例调用newInstance(...)
2.3 ReflectiveFeign#newInstance(…)
分行去分析本部分代码。
Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = this.targetToHandlersByName.apply(target, requestContext);
映射Method 和 新生成MethodHandler为Map:
public Map<Method, InvocationHandlerFactory.MethodHandler> apply(Target target, C requestContext) {Map<Method, InvocationHandlerFactory.MethodHandler> result = new LinkedHashMap();// contract解析校验元数据List<MethodMetadata> metadataList = this.contract.parseAndValidateMetadata(target.type());Iterator var5 = metadataList.iterator();while(var5.hasNext()) {MethodMetadata md = (MethodMetadata)var5.next();// 获取方法,其实就是接口中的方法封装成了多个MethodMetadataMethod method = md.method();if (method.getDeclaringClass() != Object.class) {// 创建MethodHandlerInvocationHandlerFactory.MethodHandler handler = this.createMethodHandler(target, md, requestContext);// map 映射method 和 handlerresult.put(method, handler);}}// 处理默认方法Method[] var10 = target.type().getMethods();int var11 = var10.length;for(int var12 = 0; var12 < var11; ++var12) {Method method = var10[var12];if (Util.isDefault(method)) {InvocationHandlerFactory.MethodHandler handler = new DefaultMethodHandler(method);result.put(method, handler);}}return result;}// 创建MethodHandler,默认是SynchronousMethodHandler类型的private InvocationHandlerFactory.MethodHandler createMethodHandler(Target<?> target, MethodMetadata md, C requestContext) {return md.isIgnored() ? (args) -> {throw new IllegalStateException(md.configKey() + " is not a method handled by feign");} : this.factory.create(target, md, requestContext);}
Method和MethodHandler的映射结果如下:
可以看到Method是自己定义的FeignClient接口中的一个方法。Handler是SynchronousMethodHandler
的实例。
随后将这个Map简单校验后,透传到InvocationHandler
的 dispatch
属性:
2.4 生成代理对象
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(),
new Class[]{target.type()}, handler);
以接口维度,生成对应接口的代理对象,并绑定 2.3 小节中生成的handler。
三、动态代理原理全流程梳理
以生成以下接口的代理为例。
@FeignClient(name = "helloFeignClient", url = "http://localhost:10080")
public interface HelloFeignClient {@PostMapping("/hello/post")HelloResponse postHello(@RequestBody HelloRequest helloRequest);
}
附录
附1:本系列文章链接
SpringCloud原理-OpenFeign篇(一、Hello OpenFeign项目示例)
SpringCloud原理-OpenFeign篇(二、OpenFeign包扫描和FeignClient的注册原理)
SpringCloud原理-OpenFeign篇(三、FeignClient的动态代理原理)