Spring 自动装配[1.3]ImportBDRegistrar
在前面的分析中我们知道了:
只要是@Import,那么最后执行的必然是ImportBeanDefinitionRegistrar,否则就会往下继续递归,直到都是这个类为止。
这里我们来处理之前遗留的:
类:【ImportBeanDefinitionRegistrar】
子类如何实现的?
何处处理的?
configurationClass类中,loadBeanDefinitionsForConfigurationClass在哪里,何时被调用?
子类实现及接口定义
先来看看接口中给出的方法:
public interface ImportBeanDefinitionRegistrar { default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry); } default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } } 复制代码
光从这里也看不出什么门道,只知道这里重载了两个方法,并且上面的方法调用了下面的方法,而且没用到上面的这个bean名称构造器。
从引用的地方也看出来上面重写的委实不多,重写类就俩,而这俩和SpringData关系密切:
AbstractRepositoryConfigurationSourceSupport
RepositoryBeanDefinitionRegistrarSupport
AbstractRepositoryConfigurationSourceSupport
这个类也不是具体的实现类,而是一个抽象类,但这个类基本上可以说提供了大部分数据存储数据和SpringData整合的功能:
public abstract class AbstractRepositoryConfigurationSourceSupport implements ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware { } 复制代码
因此我们重点就在下面这个方法的子类重写,看看干啥了。
在看子类方法重写之前,先看看这个方法的注释:
Register bean definitions as necessary based on the given annotation metadata of the importing @Configuration class. Note that BeanDefinitionRegistryPostProcessor types may not be registered here, due to lifecycle constraints related to @Configuration class processing. 复制代码简单点说,这个方法根据导入@Configuration类的给定注释元数据,根据需要注册bean定义。
记得开头说的吗?我们举例子就都拿OpenFeign来看了。
FeignClientsRegisrar
这里需要注意的一点是,这个类在EnableFeignClients注解里是被@Import导入到Spring容器中的:
@Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { //..... } 复制代码
直接一点来看上面子类的重写方法:
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); } 复制代码
注册默认配置
registerDefaultConfiguration(metadata, registry); private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //这里的metadata打断点进来可以看到,其实指的是启动的类,以及收集起来的那些通过feignClient注解所标注的 Map<String, Object> defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } } private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); registry.registerBeanDefinition( name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); } 复制代码
这里的代码通篇看下来没什么难度:
configuration指的其实是@EnableFeignClients里指定的defaultConfiguration,如果我们没配那就没有咯。
流程上,这里就是根据给定的metadata,构建一个BeanDefinition放入context中。这里的beanDefinition,对应的是在启动类标注的@EnableFeignClients注解中对应的配置。如果没有配置,其实这个Configuration没啥用(因为虽然会放到容器中,但里面的属性全是空)。
注册feign类
在这个方法中,会将包下所有通过**@FeignClient**标注的接口,在这里初始化。
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //这个canner是一个根据classpath去扫描的 ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); //这里是需要扫描的包的顶级目录 Set<String> basePackages; Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); //如果这里从enable的注解上找不到写的clients,那就根据metadata,找对应的顶级目录的名称 if (clients == null || clients.length == 0) { scanner.addIncludeFilter(annotationTypeFilter); basePackages = getBasePackages(metadata); } else { 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的接口,把这些接口的信息变成BD放到容器中 //这里的feignClient,对应的beanDefinition是通过FeignClientFactoryBean来的 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"); Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration")); //这个方法里就是转化并注册,里面比较有意思的一点是还会去初始化configuration registerFeignClient(registry, annotationMetadata, attributes); } } } } 复制代码
小结
在这里能看到:
实际上,这些ImportBDRegistrar,是以指定模式接收到参数,并将对应的概念、信息(比如上面的feignClients),变成BeanDefinition,注册到容器中的。
这里可能涉及到扫描、FactoryBean等相关内容。
这里可以往下继续深究的点,包括:
feign的beanDefinition既然实际上是以FeignClientFactoryBean的形式注册的,那么是如何调用的呢?
这个BDRegistrar,是在bean实例化的哪个步骤被调用的呢?
作者:BiaDisaForTruth
链接:https://juejin.cn/post/7032559899515027463