参考:ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!
@SpringBootApplication结构图如下:
首先看看这个比较底层的@Import注解
首先来看看该注解的注释说明:
Indicates one or more component classes to import — typically
@Configuration
classes.
Allows for importing@Configuration
classes,ImportSelector
andImportBeanDefinitionRegistrar
implementations, as well asregular component classes
基本作用就是导入bean,由上可知通常有四个方式:
@Configuration
注解类ImportSelector
接口实现类ImportBeanDefinitionRegistrar
接口实现类验证一下,现在有一个Student类需要注入IoC容器:
看看它的注释:
Registers packages with AutoConfigurationPackages. When no base packages or base package classes are specified, the package of the annotated class is registered.
可以看到,这个注解这里就是将org.springframework.boot.autoconfigure.AutoConfigurationPackages
注入IoC容器,原理是@Import
一个ImportBeanDefinitionRegistrar
接口的实现类,@import前后BeanDefinition变化如下两图所示:
基于Spring应用程序上下文进行自动配置,根据依赖尝试猜测并配置Bean;另外,被@EnableAutoConfiguration
注解的类所在的 p a c k a g e package package 通常作为扫描注解@Entity
的根路径,这也是为什么@SpringBootApplication
放在顶级 p a c k a g e package package 下面。
注意,注解了@EnableAutoConfiguration
的类的其他注解可以生效,如@Configuration、@Bean、@Controller
等,但是这个类之外的其他类(包括同一个包中的其他类)的注解都不会生效,扫描包中其他类注解起作用的是@ComponentScan
及其他特殊的途径。
综上,@EnableAutoConfiguration
扫描的是注解它的类和classpath下依赖包中META-INF
文件夹下spring.factories
中以org.springframework.boot.autoconfigure.EnableAutoConfiguration
为key的value类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
需要注意的是,虽然@EnableAutoConfiguration
上有个@Import
一个实现了ImportSelector
接口的类,但它却不是通过ImportSelector
接口的selectImports
方法来工作的 而是通过DeferredImportSelector
接口的内部接口Group
的void process(AnnotationMetadata metadata, DeferredImportSelector selector);
。
为什么?答案就在ConfigurationClassParser.java
(专门用于处理配置注解的类,由BeanDefinitionRegistryPostProcessor接口方法调用)中的processImports
方法,因为@EnableAutoConfiguration
上的AutoConfigurationImportSelector.class
实现了DeferredImportSelector
接口,字面意思就是延迟import,这个接口import的东西会在扫描处理完所有的@Configuration注解之后(包括依赖包中的和手动写的)再处理。
小结一下:SpringBoot先加载扫描所有注解了@Configuration
的类(同步解析@Component,@ComponentScan,@Import,@ImportResource,@Bean,@PropertySource注解),然后对于@Import(类名.class)中类如果没有继承自DeferredImportSelector
接口则再扫描后立即进行解析处理,而如果该类继承自
DeferredImportSelector
接口则不执行,等扫描完了所有的注解后再统一通过DeferredImportSelector
接口的内部接口Group
的void process(AnnotationMetadata metadata, DeferredImportSelector selector);
来执行。
看他的注释:
A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.
注释意思也是,处理完所有@Configuration
beans后再进行处理。从package org.springframework.context.annotation.ConfigurationClassParser
类中的parser方法也可以看出:
class ConfigurationClassParser {
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// parse没有注解`DeferredImportSelector`接口的@Import注解和其他那几个注解
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
// 同上
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
// 同上
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 统一处理继承自`DeferredImportSelector`接口的@Import注解
this.deferredImportSelectorHandler.process();
}
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// Process any @PropertySource annotations
...
// Process any @ComponentScan annotations
...
// Process any @ImportResource annotations
...
// Process individual @Bean methods
...
// 处理@Import
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
...
}
// Process any @Import annotations
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 如果import的类继承了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)){
// 如果是DeferredImportSelector类型,defer延迟的意思
if (selector instanceof DeferredImportSelector){
// 这里只把selector转为DeferredImportSelectorHolder存入this.deferredImportSelectors,执行不在这块,在parse方法的最后一行执行。
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
// 不是DeferredImportSelector类型就执行selectImports方法
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
}
通过调用org.springframework.context.annotation;
包下的ConfigurationClassPostProcessor
类解析加了@Configuration
、@Component
、@ComponentScan
、@Import
、@ImportResource
等注解。
凡是注解了@Configuration
的类,beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
;没有注解@Configuration
但是注解了@Component
、@ComponentScan
、@Import
、@ImportResource
的类,beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
来看代码:
//
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
...
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 本类中的方法
processConfigBeanDefinitions(registry);
}
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// beandefinition里CONFIGURATION_CLASS_ATTRIBUTE设置过了,跳过
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 这里这个最重要!!!
// 跳到ConfigurationClassUtils类中的checkConfigurationClassCandidate方法
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
...
// 解析配置类,在此处会解析配置类上的注解(ComponentScan扫描出的类,@Import注册的类,以及@Bean方法定义的类)
// 注意:这一步只会将加了@Configuration注解以及通过@ComponentScan注解扫描的类才会加入到BeanDefinitionMap中
// 通过其他注解(例如@Import、@Bean)的方式,在parse()方法这一步并不会将其解析为BeanDefinition放入到BeanDefinitionMap中,而是先解析成ConfigurationClass类
// 真正放入到map中是在下面的this.reader.loadBeanDefinitions()方法中实现的
// parse方法最后this.deferredImportSelectorHandler.process();这个就是处理延迟importSelector也就是这里加载依赖jar包下所有META-INF文件夹spring.factories类中AutoConfiguration
parser.parse(candidates);
...
// 将上一步parser解析出的ConfigurationClass类加载成BeanDefinition
// 实际上经过上一步的parse()后,解析出来的bean已经放入到BeanDefinition中了,但是由于这些bean可能会引入新的bean,例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean,或者bean中存在被@Bean注解的方法
// 因此需要执行一次loadBeanDefinition(),这样就会执行ImportBeanDefinitionRegistrar或者ImportSelector接口的方法或者@Bean注释的方法
this.reader.loadBeanDefinitions(configClasses);
}
}
abstract class ConfigurationClassUtils {
...
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
...
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
...
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 如果有@Configuration注解,beanDefinition里面某个属性设为full
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 这个isConfigurationCandidate就会从上面的candidateIndicators里面遍历比较
// 如果有candidateIndicators任意一个注解,beanDefinition里面某个属性设为lite
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
...
}
}
具体可参考ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!
继而通过解析@EnableAutoConfiguration
上的@Import(AutoConfigurationImportSelector.class)
来调用getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)
方法,里面也有个getCandidateConfigurations(annotationMetadata, attributes)
来扫描。
具体调用流程可参考:AutoConfigurationImportSelector到底怎么初始化
总而言之,该注解作用就是在依赖的包的META-INF
文件夹下的spring.factories
文件种找org.springframework.boot.autoconfigure.EnableAutoConfiguration
的值,并扫描。