springcloud源码-feign源码解析
FeignClient的使用
定义接口
1
2
3
4
5
6
7"eureka-client2",configuration = {FeignClientConfig.class}) (value =
public interface DemoClient {
"/demo/hello",method = RequestMethod.GET) (value =
public String callDemo();
}Springboot启动类中增加启动注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class,args);
}
Request.Options options() {
return new Request.Options(1000*10,40*1000);
}
}在使用的时候,直接自动化注入@Autowired。
方法说明:英文阅读,不翻译了
1 | /** |
原理解析
先说下大概到思路:
1、定义使用feignclient的注解接口
2、启动类中使用Import类,导入FeignClient的实现机制。
3、这个导入类会去初始化Feign的配置文件,进行一些环境变量的初始化和准备工作。
4、创建一个BeanFactory,在程序中@AutoWired的时候,就getObject()。这个getObject就是创建一个FeignClient的代理类。
5、因为接口方式,所以采用的是jdk动态代理实现。
6、实际调用,由代理类进行直接调用。
EnableFeignClients注解
- 这个注解类里的方法就暂时忽略不看了,大家自己看看,基本是扫描的package定义,还有就是指定哪些客户端生效的配置等。
- Import注解导入了FeignClientsRegistrar类。这个在springboot用的特别多。
FeignClientsRegistrar类
- 实现了ImportBeanDefinitionRegistrar这个接口,他的作用基本就是注册一个bean进容器。
- 其他接口基本是一些loader的中间件接口,还有环境变量中间件。
这个类主要做了两件大事:
1、初始化配置。
2、注册feignclient客户端。1
2
3
4
5
6
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
registerDefaultConfiguration方法:
- 对于registerDefaultConfiguration,主要是加载默认配置。由于EnableFeignClients中有一些方法来对feign进行全局配置,所以,这里就是注册全局的配置。
- 通过AnnotationMetadata拿到注解的属性和属性值。
- 对于defaultConfiguration这个自定义的配置类,进行单独的处理。
- 对于这些自定义的配置,会封装在FeignClientSpecification类中,然后注册进spring容器中。
registerFeignClients方法:
1 | public void registerFeignClients(AnnotationMetadata metadata, |
看下核心方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
//定一个FeignClientFactoryBean的builder。
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
//这个name基本是http://service-id/....
String name = getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
//注册了一个FeignClientFactoryBean
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
接下来,需要看看FeignClientFactoryBean这个类:
这类实现了FactoryBean接口,getObject接口返回实例化的对象。
我们来看下这个类的代码:
1 | /** |
看下loadBalance方法吧:
- 这里基本就是返回了一个Proxy代理类。代理类这个接口所有的方法的执行。
- Targeter就是实现类HystrixTargeter。
- 对于Client,有一个实现类是LoadBalanceFeignClient。
1 | protected <T> T loadBalance(Feign.Builder builder, FeignContext context, |
- 我们看下handler:SynchronousMethodHandler
这个里面的invoke方法,就是代理的真正执行方法。在执行feign方法的时候,会调用invoke执行。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
}
}
本文作者 : braveheart
原文链接 : https://zhangjun075.github.io/passages/springcloud-feign/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
知识 & 情怀 | 二者兼得