介绍
简单的说,就是在spring应用启动过程,一些被@Import注解的类(这些类都实现了ImportBeanDefinitionRegistrar接口)会执行ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,然后生成BeanDefinition对象,并最终注册到BeanDefinitionRegistry中,为后续实例化bean做准备的。
在哪里用到了?
以我们非常熟悉的spring boot应用为例,启动类就包含了,
@SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
其中@Import(AutoConfigurationImportSelector.class)就是。
另外还有spring集成mybatis的注解**@MapperScan**一样也用到。
使用过程解析
进入org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
进去getImports方法
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
这里的SourceClass是当前的配置类,什么是配置类?配置类就是包含@Configuration、@Service、@Component等注解的类,比如我们的spring boot启动类。
上面这段代码相当于是每个配置类都去找一遍是否包含@Import注解。
出来接着上面的processImports方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(
configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
这里也很简单,就是几个if else分支判断,
其中
if(candidate.isAssignable(ImportBeanDefinitionRegistrar.class))
是不是看到ImportBeanDefinitionRegistrar接口了,这里就是把上面找到的Import类添加到
org.springframework.context.annotation.ConfigurationClass#importBeanDefinitionRegistrars这个Map中。
顺便说一下另外的
if (candidate.isAssignable(ImportSelector.class))这句就是我们熟悉用来解析spring boot自动装配用的,只不过它是ImportSelector类型,这种不放到Map中,后面会延迟单独处理它。
上面的查找过程,是每一个配置类都走一遍,如果有ImportBeanDefinitionRegistrar,就加到各自里面的Map先缓存起来。
既然存起来了,那肯定有用的地方是吧, 接着看。
先说明下ConfigurationClassPostProcessor这个类,很重要,它是解析所有配置类的,将配置类都转换成BeanDefinition对象,后续创建bean就靠它, 上面调用链路也是由它发起的。
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
其中有一句:
this.reader.loadBeanDefinitions(configClasses);
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
遍历每一个ConfigurationClass 配置类,因为我们前面已经是解析过ImportBeanDefinitionRegistrar了,所以这些配置类是包含ImportBeanDefinitionRegistrar类的。
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
好了,这里终于要拿前面缓存好的Map了
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
遍历每一个ImportBeanDefinitionRegistrar类,调用registerBeanDefinitions方法完成后续注册工作。