前言:
在上一篇文章Spring Cloud OpenFeign源码加载原理
我谈到registerFeignClients方法注册的bean是FeignClientFactoryBean类型,此时的Bean只是注册到容器中尚未实例化,这篇文章我们来看看FeignClientFactoryBean实例化都发生了什么事情。
源码分析:
我们先看看FeignClientFactoryBean的类图结构:
可以看出FeignClientFactoryBean实现了三个接口,分别是FactoryBean、InitializingBean、ApplicationContextAware,实现了FactoryBean接口意味着在实例化时候会调用getObject方法一个定义的对象而不是注册到容器中的Bean类型;实现了InitializingBean接口意味着Bean属性设置完成以后会调用afterPropertiesSet方法进行一些业务逻辑;实现了ApplicationContextAware表示Spring容器会给该对象注入ApplicationContext对象,即容器对象。
那么我们重点关注FeignClientFactoryBean的getObject方法,该方法的代码如下:
@Override
public Object getObject() throws Exception {
return getTarget();
}
可以看出该getObject方法又调用了getTarget方法,该方法的代码如下:
<T> T getTarget() {
FeignContext context = this.applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
this.url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient) client).getDelegate();
}
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();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
首先从容器中获取FeignContext对象,这个对象又是什么时候加载到Spring容器中的那?
它是在引入了OpenFeign的Pom依赖以后自动引入了openfegin-core包的jar包,在该jar包的spring.factories文件中引入了一个自动配置类是FeignAutoConfiguration,在该自动配置类中又@Autowired List<FeignClientSpecification>和导入一个FeignContext的Bean对象,截图如下:
可以看出FeignContext继承了NamedContextFactory类,FeignClientSpecification实现了NamedContextFactory.Specification接口,这两个类的设计和Ribbon源码的设计理念是一样的,关于这两个类是做什么的,可以观看我的文章SpringBoot整合Ribbon源码分析之核心组件加载原理
我们继续回到getTarget方法中调用了feign方法,该方法的代码如下:
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// @formatter:on
configureFeign(context, builder);
return builder;
}
这里面又调用了一个get方法,get方法主要是从FeignContext子容器中取出是FeignLoggerFactory类型的类,这个是在org.springframework.cloud.openfeign.FeignClientsConfiguration中配置的,默认实现是DefaultFeignLoggerFactory,接着调用该对象的create方法创建一个Logger,这个Logger是Feign自己定义的而不是Slf4j的。
接着又从FeignContext子容器中取出是Feign.Builder类型的类,这些Bean同样是org.springframework.cloud.openfeign.FeignClientsConfiguration中配置的,在这个Feign内部类的Build类里面有几个重要的成员属性,它们分别是:
1、List<RequestInterceptor>
这个RequestInterceptor类看名字可以猜测它是一个拦截器,主要是远程请求Feign时候添加一些请求头参数用的,它没有默认实现,需要我们自己往容器中添加。
2、Encoder encoder
这个类负责编码一个Object对象到一个Http请求识别的类型,例如把Object转成Byte,json等等,默认实现是PageableSpringEncoder。
3、Decoder decoder
这个类负责解码Feign远程调用返回的Response对象,把它解码成指定的Object对象,默认实现是OptionalDecoder。
4、boolean decode404
是否处理404的标识,默认是false。
接着调用configureFeign方法,该方法主要是看用户是否配置了一下自定义的组件,如果设置了就把自定义的组件加载到Feign.Builder类上,这个方法就不分析了,感兴趣的可以自己看看这个方法。
我们继续看getTarget方法,往下执行获取到url,是拼接上http:,我这个例子完整的url是http://exerice,接着创建一个HardCodedTarget对象,该对象的参数主要有Class<T> type、String name、String url,type是标有@FeignClient接口类,name是@FeignClient注解的value属性值,url是完整的http://exerice。
接着调用loadBalance方法,该方法的代码如下:
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
首先从容器中取得Client实现了,实现类是LoadBalancerFeignClient,把LoadBalancerFeignClient赋值到Feign.Builder对象属性中,在从容器中获取类型为Targeter的实现类是HystrixTargeter,然后调用其target方法,该方法的代码如下:
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
: factory.getContextId();
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(name, context, target, builder, fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(name, context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
判断Feign.Builder不是feign.hystrix.HystrixFeign.Builder类型,调用Feign.Builder的target方法,该方法的代码如下:
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
可以看到build方法里面创建一个SynchronousMethodHandler.Factory对象,然后把Feign.Builder一些client、requestInterceptors属性赋值给该对象的属性,又创建一个ParseHandlersByName对象,把Feign.Builder一些encoder、decoder、SynchronousMethodHandler.Factory等一些属性赋值给ParseHandlersByName对象的属性,然后在创建一个ReflectiveFeign对象,把ParseHandlersByName对象传入到ReflectiveFeign对象的属性。
我们继续看newInstance方法,该方法的代码如下:
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
首先调用了ParseHandlersByName对象的apply方法,该方法主要是处理Feign客户端接口上的注解,如 @RequestMapping、@RequestHeader、@RequestParam 等,将它们解析为 Feign 中可用的配置信息,以便在发起 HTTP 请求时使用。由于这个方法代码量很大就不在此做分析了。
接着创建一个数据结构是Map<Method, MethodHandler>的对象,遍历标有@FeginClient注解的接口类的方法,这个方法是通过反射获取的,是java.lang.reflect.Method对象,放入到Map中,key是java.lang.reflect.Method对象对象,value是MethodHandler对象,该对象是通过ParseHandlersByName的apply方法封装而来的。
接着调用feign.InvocationHandlerFactory.Default的create方法,该方法创建了一个FeignInvocationHandler并传入属性Target target、Map<Method, MethodHandler> dispatch,该类的定义如下图所示:
可以看到这个对象实现了InvocationHandler接口,由此可知在生成完动态代理后调用接口方法时候会调用该对象的invoke方法。
最后调用T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler)这段代码生成一个接口的类的代理类返回,至此整个FeignClientFactoryBean的getObject方法就返回了,返回的是接口的代理类。
总结:
FeignClientFactoryBean的getObject方法最终会返回一个接口的代码类,是用的JDK的动态代理方式生成的代理,在生成接口代理类时候最重要的参数是feign.ReflectiveFeign.FeignInvocationHandler,它是实现了InvocationHandler,会调用其invoke方法,这个对象的成员属性Map<Method, MethodHandler> dispatch已经封装了所有的Feign方法,然后当我们请求的时候就可以进行远程调用,下一节我会分析具体的调用源码,好了,我是爱编程的老徐,如果喜欢我的文章请点赞关注,我们下期见。
本文暂时没有评论,来添加一个吧(●'◡'●)