<a name="HmTEj"></a>
1 先画一张图,借助feign调用接口的过程
2 Feign源码学习
学习Feign的突破入口应该在哪里?<br />第一个入口,启动类的注解@EnableFeignClients
<br />第二个入口,我们自定义的接口的注解,@FeignClient
<br />第二个入口的作用, 可以猜想到是由feign核心机制来扫描的,扫描到的接口,会生成动态代理,以及解析和处理接口上打的那些Spring web mvc 的注解,生成Http请求<br />同时也可以猜想到,第一个入口的注解,会触发对应的feign的核心机制,那个被触发的核心机制,会扫描所有包下面的被@FeignClient
注解的接口。
在@EnableFeignClients
注解中,有@Import(FeignClientsRegistrar.class)
<a name="q3loy"></a>
2.1 FeignClientsRegistrar
FeignClientsRegistrar实现了ImportBeanDefinationRegistrar接口,实现了以下方法<br />这个接口是Spring-context项目中带的。可以猜想的到,肯定是spring-boot启动时,spring容器初始化的时候,在某个时间点,会对实现这个接口的类的方法进行调用,来实例化一些bean,注册到spring容器中去。
@Override
public void registerBeanDefinations(AnnotationMetadate metadata,
BeanDefinitionRegistry registry){
//解析相关配置,注册到自己身体里面去
registerDefaultConfiguration(metadata,registry);
//扫描各个包下面的@FeignClient注解,生成动态代理
registerFeignClients(metadata,registry);
}
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry){
//获取一个组件扫描期
ClassPathScannerCandidateComponentProvider scanner = getScanner();
//从meta中获取EnableFeignClients注解的参数
Map<String,Object> attrs = metadata.getAnnotationAttributes(
EnableFeignClients.class.getName());
//...
//生成一个只过滤FeignClient注解的过滤器
AnnotationTypeFilter annotationTypeFilter =
new AnnotationTypeFilter(FeignClient.class);
//从EnableFeignClients注解的参数中获取"clients",这是在指定那些被认为FeignClient
//但是一般不设置,为空。Class<?>[] clients
if(clients==null || clients.length == 0) {
//组件扫描器和过滤器关联
scanner.addIncluderFilter(annotationTypeFilter);
//根据元数据获取药扫描的包
basePackages = getBasePackages(metadata);
}
for(String basePackage : basePackages){
//从packages中找到候选的组件
Set<BeanDifinition>candidateComponents =
scanner.findCandidateComponents(basePackages);
//
}
}
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata){
Set<String> basePackages;
//获取元数据
//如果元数据中配了value\basePackages\basePackageClasses,
//则转换为包路径,添加到basePackages中
//如果还是为空,则把@EnableFeignClients注解所在包
//(也就是Application启动类所在包)的路径加进来
//比如com.zhss.services
if(basePackages.isEmpty()){
basePackages.add(
ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
}