FeignClientRegistrar 做的第二件事是扫描指定包下的类文件,注册 @FeignClient 注解修饰的接口类信息:
//FeignClientsRegistrar#registerDefaultConfiguration
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//生成自定义的ClassPathScanningCandidateComponentProvider
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
//获取@EnableFeignClients所有属性的键值对
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
//使用@FeignClient作为类型进行过滤,即只扫描出被@FeignClient修饰的类
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
//如果@EnableFeignClients没有设置clients属性,那么需要扫描basePackages,所以使用AnnotationTypeFilter从basePackages中扫描
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
//设置TypeFilter
scanner.addIncludeFilter(annotationTypeFilter);
//获取basePackages
basePackages = getBasePackages(metadata);
}
else {
//如果配置了clients属性,则从clients指定的类的路径下查找
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
for (Class<?> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
@Override
protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(
new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
}
//扫描包路径,获取使用@FeignClient注解修饰的接口类
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
//获取@FeignClient注解的属性键值对
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
//获取client的名称
String name = getClientName(attributes);
//单独注册每个@FeignClient中的configuration配置类
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//注册@FeignClient的接口类的BeanDefinition
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
如上述代码所示, FeignClientsRegistrar 的 registerFeignClients
方法依据 @EnableFeignClients 的属性,通过getBasePackages
获取要扫描的包路径信息,然后获取这些包下所有被 @FeignClient 注解修饰的接口类的 BeanDefinition, 最后调用 registerFeignClient
动态注册 BeanDefinition。
可以看下getBasePackages
方法:
//FeignClientsRegistrar#getBasePackages
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
//获取values属性值
Set<String> basePackages = new HashSet<>();
for (String pkg : (String[]) attributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
//获取basePackages属性值
for (String pkg : (String[]) attributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
//获取basePackageClasses属性值
for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
//获取@EnableFeignClients注解修饰的类的全路径
if (basePackages.isEmpty()) {
basePackages.add(
ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
它根据 @EnableFeignClients 注解的value
、basePackages
、basePackageClasses
属性来判断要扫描的路径,如果有则加入到backPackages
属性中,如果没有则获取使用 @EnableFeignClients 注解修饰的类的全路径加入basePackages
。
OpenFeign 使用 ClassPathScanningCandidateComponentProvider 和各类 TypeFilter 来过滤出被 @FeignClient 修饰的类,getScanner
方法如下:
//FeignClientsRegistrar#getScanner
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(
AnnotatedBeanDefinition beanDefinition) {
boolean isCandidate = false;
//判断beanDefinition是否为内部类
if (beanDefinition.getMetadata().isIndependent()) {
//判断是否为接口类,并且该接口不为Annotation类型,则返回true,否则返回false
if (!beanDefinition.getMetadata().isAnnotation()) {
isCandidate = true;
}
}
return isCandidate;
}
};
}
ClassPathScanningCandidateComponentProvider 的作用是遍历指定路径的包下的所有类。比如指定包路径为 aaa/bbb/ccc
,它会找出 aaa.bbb.ccc
包下所有的类,将所有的类封装成 Resource 接口集合。Resource 接口是 Spring 对资源的封装,有 FileSystemResource、 ClassPathResource、 UrlResource 等多种实现。 接着 ClassPathScanningCandidateComponentProvider 类会遍历 Resource 集合,通includeFilters
和 excludeFilters
两种过滤器进行过滤操作。 includeFilters
和 excludeFilters
是 TypeFilter 接口类型实例的集合,TypeFilter 是一个用于判断类型是否满足要求的类型过滤器。 excludeFilters
中只要有一个 TypeFilter 满足条件,那么这个 Resource 就会被过滤掉;而 includeFilters
中只要有一个 TypeFilter 满足条件,这个 Resource 就不会被过滤。 如果一个 Resource 没有被过滤,它会被转换成 ScannedGenericBeanDefinition 添加到 BeanDefinition 集合中。
//ClassPathScanningCandidateComponentProvider#scanCandidateComponents
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//根据basePackages生成需要搜索的路径
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//将basePackages下所有的类封装成Resource类集合
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
//遍历Resource
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//使用excludeFilters和includeFilters进行过滤
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
通过调试可以看到扫描的路径是basePackages,如下图:
而通过路径扫描到的类有如下:
根据图上看到,scanCandidateComponents
方法会扫描路径下的所有类文件(*.class),不管是 Bean 实例,还是普通的 Java 类,都会被扫描到。
接着方法会遍历 Resource 集合, 并通过includeFilter
和 excludeFilter
属性进行过滤,方法为isCandidateComponent
:
//ClassPathScanningCandidateComponentProvider#isCandidateComponent
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//排除
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
//包括
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
通过isCandidateComponent
方法,会过滤出满足条件的配置类,由此会筛选出带有 @FeignClient 注解修饰的FeignClient 接口类。
for (String basePackage : basePackages) {
//内部调用ClassPathScanningCandidateComponentProvider#scanCandidateComponents
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
//获取@FeignClient注解的属性键值对
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
//获取服务名
String name = getClientName(attributes);
//将@FeignClient上的configuration注册到服务名name上
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//注册FeignClient接口类
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
循环遍历 BeanDefinition ,获取每个 FeignClient 接口类上的configuration
属性,并进行注册。该注册方法同 @EnableFeignClients 注册默认配置类一样,都是调用同一个方法registerClientConfiguration
,只不过此处会注册 @FeignClient 上的configuration
对应的配置类,同样注册了一个类为 FeignClientSpecification 的 Bean 实例,Bean的name
为xxx.FeignClientSpecification
,而xxx
为使用 @FeignClient 注解的接口类名称,可以根据 @FeignClient 注解的contextId
、value
、name
、serviceId
属性的其中一个获取。
接着调用registerFeignClient
方法进行 FeignClient接口类对象的注册:
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);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
//依次顺序判断serviceId、name、value的值作为name
String name = getName(attributes);
definition.addPropertyValue("name", name);
//如果配置了contextId属性,则使用contextId,否则使用name
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
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 = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
//默认为true
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 });
//注册bean
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
registerFeignClient
的功能就是解析 **@FeignClient **注解的属性,并对该 FeignClient 接口类进行注册,注册的 Bean 类型为 FeignClientFactoryBean,并依次将 **@FeignClient ** 属性设置到 FeignClientFactoryBean 实例中,用于后期实例的获取。
//BeanDefinitionReaderUtils#registerBeanDefinition
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
微信公众号:Java知识集训