参考:ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!
@SpringBootApplication结构图如下:
首先看看这个比较底层的@import注解
首先来看看该注解的注释说明:
Indicates one or more component classes to import — typically @Configuration classes.
Allows for importing @Configuration classes, importSelector and importBeanDefinitionRegistrar implementations, as well as regular component classes
基本作用就是导入bean,由上可知通常有四个方式:
- @Configuration 注解类importSelector接口实现类importBeanDefinitionRegistrar接口实现类普通java类
验证一下,现在有一个Student类需要注入IoC容器:
方式1:
方式2:
方式3:
方式4:
看看它的注释:
@EnableAutoConfigurationRegisters 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方法来工作的。为什么?答案就在ConfigurationClassParser.java(专门用于处理配置注解的类)中的processimports 方法,因为@EnableAutoConfiguration上的AutoConfigurationimportSelector.class实现了DeferredimportSelector接口,字面意思就是延迟import,这个接口import的东西会在扫描处理完程序员自己写的包之后再去import,这样做:spring要先把程序员写的东西都处理完,然后再扫描处理依赖,比如,程序员自定义了很多的configuration,而在依赖包中本来就存在一些configuration,而依赖中一般会有类似@ConditionOnMissingBean这样的注解,确保自定义的生效,默认配置的不生效。所以要先扫描处理程序员写的包,再扫描处理依赖,而DeferredimportSelector接口就是起到了这么个作用,你看看他的注释:
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.
。
// Process any @import annotations class ConfigurationClassParser { processimports(configClass, sourceClass, getimports(sourceClass), filter, true); private void processimports(ConfigurationClass configClass, SourceClass currentSourceClass, CollectionimportCandidates, Predicate 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 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) { ListconfigCandidates = 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 SetcandidateIndicators = 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 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的值,并扫描。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)