文章目录
- 前言
- 正文
- 一、前戏,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;
}
// 处理url
String url = this.url + cleanPath();
// 通过工厂获取client实例
Client client = getOptional(feignClientFactory, Client.class);
// 生成client
if (client != null) {
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((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
。
默认实现如下:
@Override
public <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();
// 获取方法,其实就是接口中的方法封装成了多个MethodMetadata
Method method = md.method();
if (method.getDeclaringClass() != Object.class) {
// 创建MethodHandler
InvocationHandlerFactory.MethodHandler handler = this.createMethodHandler(target, md, requestContext);
// map 映射method 和 handler
result.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的动态代理原理)