【摘要】 Feign是一个声明式RESTful HTTP请求客户端,它使得编写Web服务客户端更加方便和快捷。使用Feign创建一个接口并使用Feign提供的注解修饰该接口,然后就可以使用该接口进行RESTful HTTP请求的发送。Feign还可以集成Ribbon和Eureka来为自己提供负载均衡和断路器的机制。接着上一篇,我们继续讲解动态注册BeanDefinition`。registerFeig…
Feign是一个声明式RESTful HTTP请求客户端,它使得编写Web服务客户端更加方便和快捷。使用Feign创建一个接口并使用Feign提供的注解修饰该接口,然后就可以使用该接口进行RESTful HTTP请求的发送。Feign还可以集成Ribbon和Eureka来为自己提供负载均衡和断路器的机制。
接着上一篇,我们继续讲解动态注册BeanDefinition`。
registerFeignClients函数中有一些实现上的细节值得大家认真了解并学习的,有利于大家对Spring框架的深度了解。首先是如何自定义Spring类扫描器。也就是如何使用ClassPathScanningCandidateComponentProvider的自定义和各类TypeFilter`。
ClassPathScanningCandidateComponentProvider的基本原理为:
-
遍历basePackages,根据每个basePackage找出这个包下的所有的class。比如basePackage为com/test,会找出com.test包下所有的class。找出之后封装成Resource接口集合,这个Resource接口是Spring对资源的封装,有
FileSystemResource、ClassPathResource、UrlResource实现等。 -
遍历找到的
Resource集合,通过includeFilters和excludeFilters判断是否解析。这里的includeFilters和excludeFilters是TypeFilter接口类型的集合,是ClassPathBeanDefinitionScanner内部的属性。TypeFilter接口是一个用于判断类型是否满足要求的类型过滤器。excludeFilters中只要有一个TypeFilter满足条件,这个Resource就会被过滤。includeFilters中只要有一个TypeFilter满足条件,这个Resource就不会被过滤。 -
如果没有被过滤。把Resource封装
成ScannedGenericBeanDefinition添加到BeanDefinition结果集中,然后返回。
在上边的代码中,Feign就使用了AnnotationTypeFilter,来过滤出被@FeignClient修饰的类。而getScanner函数的具体实现如下所示
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(
AnnotatedBeanDefinition beanDefinition) {
//判断beanDefinition是非内部类,否则直接返回false
if (beanDefinition.getMetadata().isIndependent()) {
//判断是否为接口类,并且所实现的接口只有一个,并且该接口是Annotation.否则直接返回true
if (beanDefinition.getMetadata().isInterface()
&& beanDefinition.getMetadata()
.getInterfaceNames().length == 1
&& Annotation.class.getName().equals(beanDefinition
.getMetadata().getInterfaceNames()[0])) {
try {
Class<?> target = ClassUtils.forName(
beanDefinition.getMetadata().getClassName(),
FeignClientsRegistrar.this.classLoader);
return !target.isAnnotation();
}
catch (Exception ex) {
this.logger.error(
"Could not load target class: "
+ beanDefinition.getMetadata().getClassName(),
ex);
}
}
return true;
}
return false;
}
};
}
第二个细节实现是getBasePackages的实现,了解它有利于你更好的了解@EanbleFeignClients的各个属性配置。
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
Set<String> basePackages = new HashSet<>();
//basePackage包括value的属性值
for (String pkg : (String[]) attributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
//basePackage包括basePackages的属性值
for (String pkg : (String[]) attributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
//basePacakge包括basePackageClasses的属性值的包名
for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
//如果都为空,那么就将@EnableFeignClients所修饰的类的所在包为basePackage
if (basePackages.isEmpty()) {
basePackages.add(
ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
第三个细节是对被@FeignClient修饰的接口类FeignClient的Bean信息的处理。在registerFeignClient函数中,Feign将FeignClientFactoryBean作为这些FeignClient的基础Bean信息注册到了Spring容器中,并且将相关的属性进行设置。
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
//FeignClientFactoryBean是工厂类,用来生成bean
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
//将@FeignClient的属性设置到BeanDefinition的PropertyValue中。
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
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");
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
//使用BeanDefinitionReaderUtils进行注册。
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
接下来将会关注实例初始化,FeignClientFactoryBean是工厂类,Spring容器通过调用它的getObject函数来获取对应的Bean实例。我们将会解析这部分的实现。






















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)