FeignAutoConfiguration是怎么被收录的?
不是通过@EnableFeignClients,那么是根据jar包判断,那么是怎么回事?
Feign.Builder是怎么收录的?getBean得不到,但是在FeignClientFactoryBean的getObject方法中,是可以从spring工厂获取的
FeignAutoConfiguration里面,
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
如果有HystrixFeign这个类,就用HystrixTargeter,如果没有那就DefaultTargeter。Targeter是在FeignClientFactoryBean的getObject方法中,调用target方法,返回实际的feignclient类,是default feignclient或者hystrixCommand的入口。在里面调用builder的target方法,---->返回ReflectiveFeign--->newInstance方法中,逻辑写在中间类中,返回代理类。在Hystrix的中间类HystrixInvocationHandler中,invoke方法,创建一个hystrixCommand,调用并返回execute方法。
ReflectiveFeign中的newInstance方法,Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target),创造一个methodHandler,把接口的抽象方法,对应成一个实际远程调用的方法。
这个methodHandler是怎么生成的?DefaultTargeter和HystrixTargeter区别就是最后newInstance方法中,使用的动态代理中间类不同。
DefaultTargeter是用的Feign的内部类的builder,build方法返回的ReflectiveFeign传入的参数是通过下面得到。
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
而HystrixTargeter的builder继承了Feign.builder,但是build方法使用了自己的InvocationHandler,一样的是根据抽象方法名,找到实际的http方法,但是有fallback,有hystrix的熔断,隔离等机制
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
}
});
super.contract(new HystrixDelegatingContract(contract));
return super.build();
}
先看DefaultTargeter的生成methodHandler的逻辑。
nameToHandler是一个map,String----Methodhandler,ParseHandlersByName类的apply方法,在这里,根据接口的method和它上面的注解,生成具体的,可以执行http远程调用的方法===>
先用contract过滤一下,提取中间不是default的抽象方法,buildTemplate封装一下参数啥的,最关键的是factory.create(key, md, buildTemplate, options, decoder, errorDecoder)方法,new 一个SynchronousMethodHandler,
调用其中的invoke方法,完成请求
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
就是从接口方法里面提取url,参数,等待,发起http请求,用connection。
但是怎么和eureka结合的呢?
在spring工厂中的实现feign.Client接口的默认类是ribbon的LoadBalancerFeignClient,这个client是怎么被放到spring工厂的,还是个未解的难题
System.err.println(ac.getBean(Client.class));
org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient@553da911
但是 client是怎么被赋值给builder的呢?
在factoryBean的getObject() 方法中,
@Override
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
String url;
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
return loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, 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);//从spring工厂取出client,按照上面说的,是ribbon的,检测类型,赋值给builder(建造者模式?build)
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not lod balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
Feign.Builder.class 在run返回的applicationcontext中找不到,但是FactoryBean中可以获取到,是有一个子applicationContext,parent是那个大的spring工厂,那么这个子工厂是什么时候建立的呢?
FeignContext extends NamedContextFactory---->实现了ApplicationContextAware接口,把parent属性赋值大spring工厂。
FeignAutoConfiguration里面会实例化FeignContext,调用构造函数的时候给NamedContextFactory的构造参数传入了FeignClientsConfiguration.class,在FactoryBean调用feign()方法获取的时候会调用getcontext,
去自己的子工厂去找,如果没有子工厂,创建一个,FeignClientsConfiguration里面的bean都会被实例化