1.@Import
注解在springBoot中间接的广泛应用
在springboot中并没有直接显式的使用@Import
标签,而是通过@Import
标签来间接的提供了很多自动配置的注解。比如@EnableAutoConfiguration
,@EnableConfigurationProperties
等。这些标签的实现都是通过使用@Import
标签来完成的。
......
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
......
}
......
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
......
}
可以发现都是通过@Import
来完成的。
2.spring中的@Import
注解
2.1@Import
注解的作用
@Import
标签可以导入一个或者多个组件类,通常是@Configuration
注入的bean。提供了与xml中<import/>
标签类型的功能,能够导入@Configuration
类。
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
导入的类可以是@Configuration
类型的配置类,实现了ImportSelector
接口的类,实现了ImportBeanDefinitionRegistrar
的类或者常规的组件类。
2.2@Import
的解析前的处理
@Import
处理的位置其实跟@Conditional
注解都在同一个类中,处理的时机也是一样的。这里可以去看看@Conditional
注解解析的逻辑。
2.2.1 容器的刷新时候的准备
在容器刷新方法就定义在AbstractApplicationContext
类的refresh
方法中。在这个里面会有解析注册以及实例话bean和其他的步骤,我们要看的就是解析跟注册步骤。
public void refresh() throws BeansException, IllegalStateException {
......
invokeBeanFactoryPostProcessors(beanFactory);
}
在invokeBeanFactoryPostProcessors
方法中会实例化所有的BeanFactoryPostProcessor
类型的类并调用实现的postProcessBeanFactory
方法。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
......
}
2.2.2 PostProcessorRegistrationDelegate
处理BeanDefinitionRegistry
以及BeanFactoryPostProcessor
这里直接进入到invokeBeanFactoryPostProcessors
方法。会有两种处理方式:
- 当前的
beanFactory
是BeanDefinitionRegistry
类型的
(1) 先处理beanFactory
,然后按照是否实现了PriorityOrdered
,Ordered
以及两个都没有实现的顺序来处理BeanDefinitionRegistryPostProcessor
接口的实现类。
(2)处理实现了BeanFactoryPostProcessor
接口的子类依次调用实现的postProcessBeanFactory
方法 - 当前的
beanFactory
不是BeanDefinitionRegistry
类型的,则直接处理实现了BeanFactoryPostProcessor
接口的子类依次调用实现的postProcessBeanFactory
方法
其中会处理@Import
标签的ConfigurationClassPostProcessor
实现类BeanDefinitionRegistryPostProcessor
接口跟PriorityOrdered
接口间接实现了BeanFactoryPostProcessor
接口因此无论怎么样都会被调用的。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
//如果当前的beanFactory是BeanDefinitionRegistry的,则需要将已经存在的beanDefinition进行注册
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
//保存注册bean的BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
//迭代beanFactoryPostProcessors如果是BeanDefinitionRegistryPostProcessor子类则加入到registryProcessors集合中
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
//处理beanFactory中的beanDefinitionNames
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
//先处理同时实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的实现类
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
//如果当前的BeanDefinitionRegistryPostProcessor类的实现类也实现了PriorityOrdered类,则加入当当前需要注册的BeanDefinitionRegistryPostProcessor集合
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
//进行排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
//加入到registryProcessors集合
registryProcessors.addAll(currentRegistryProcessors);
//调用实现了BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
......
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
}
2.2.3 ConfigurationClassPostProcessor
的processConfigBeanDefinitions
跟processConfigBeanDefinitions
解析bean
上面提到了ConfigurationClassPostProcessor
无论怎么样都会被调用。这里先看看实现的postProcessBeanDefinitionRegistry
方法跟postProcessBeanFactory
的共同点。
//在refresh方法中最先被调用
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//给当前的BeanDefinitionRegistry类型的beanFactory设置一个全局hash值,避免重复处理
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
//加入到已经处理的PostProcessed集合中
this.registriesPostProcessed.add(registryId);
//进行处理
processConfigBeanDefinitions(registry);
}
/**
* Prepare the Configuration classes for servicing bean requests at runtime by replacing them with CGLIB-enhanced subclasses.
*/
//在refresh方法中第二被调用
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//给当前的ConfigurableListableBeanFactory类型的beanFactory设置一个全局hash值,避免重复处理
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
//加入到已经处理的集合中
this.factoriesPostProcessed.add(factoryId);
//如果当前的beanFactory不在registriesPostProcessed中则进行处理
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
//将beanFactory中用Configuration注解配置的bean进行动态加强
enhanceConfigurationClasses(beanFactory);
//增加一个ImportAwareBeanPostProcessor
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
发现一个共同点就是,如果没有解析过的beanFactory
进来都会调用processConfigBeanDefinitions
方法来处理Configuration
类。在这个方法中有两个解析的位置。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//循环处理知道所有的bean都处理完,包含bean内部定义的bean
do {
//第一个解析的位置,这里是解析能够直接获取的候选配置bean。可能是Component,ComponentScan,Import,ImportResource或者有Bean注解的bean
parser.parse(candidates);
parser.validate();
//获取上面封装已经解析过的配置bean的ConfigurationClass集合
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
//移除前面已经处理过的
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//第二个解析的位置,这里是加载configurationClasse中内部可能存在配置bean,比如方法上加了@Bean或者@Configuration标签的bean
this.reader.loadBeanDefinitions(configClasses);
......
}
......
}
两个解析的位置分别是:
- 第一个位置是
ConfigurationClassParser
类的parse
方法,解析能够直接从beanFactory
中获取的候选bean。 - 第二个是
ConfigurationClassBeanDefinitionReader
的loadBeanDefinitions
方法,解析从直接获取的候选bean中内部定义的bean。
2.2.4 ConfigurationClassParser
的解析过程
ConfigurationClassParser
的parse
有很多重载的方法,但是内部逻辑都是调用的processConfigurationClass
方法。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
//解析bean
if (bd instanceof AnnotatedBeanDefinition) {
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类型的实现类
this.deferredImportSelectorHandler.process();
}
protected final void parse(@Nullable String className, String beanName) throws IOException {
......
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
从上面可以看出来,主要要看的逻辑还是在processConfigurationClass
方法中。这里还需要注意一点就是在最上面的parse
方法最后有一个逻辑this.deferredImportSelectorHandler.process()
这个逻辑是处理@Import
注解中指定的引入类是DeferredImportSelectorHandler
子类的情况的。
进入到processConfigurationClass
方法,关于这个方法在@Conditional
注解解析的逻辑这个里面说到过。这里只需要关注内部的doProcessConfigurationClass
方法的逻辑。
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
//检查当前解析的配置bean是否包含Conditional注解,如果不包含则不需要跳过
// 如果包含了则进行match方法得到匹配结果,如果是符合的并且设置的配置解析策略是解析阶段不需要调过
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
//从缓存中尝试获取当前配置bean解析之后的ConfigurationClass对象
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
//检查当前这个配置bean是通过@Import标签引入的还是自动注入到另外一个配置类bean里面的
if (configClass.isImported()) {
//如果是通过@Import标签引入则将当前解析的配置bean加入到已经存在的解析过的bean的用来保存通过@Import标签引入的bean的集合中
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
//将当前解析的配置bean代替之前的
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
//递归获取原始的配置类信息然后封装为SourceClass
SourceClass sourceClass = asSourceClass(configClass);
do {
//处理configClass
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
//保存到configurationClasses中
this.configurationClasses.put(configClass, configClass);
}
在doProcessConfigurationClass
方法中有很多标签的处理逻辑,比如@Component
,@PropertySources
,@ComponentScans
等。对于@Import
注解的处理方式,封装在了另外的一个方法processImports
里面。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
.......
processImports(configClass, sourceClass, getImports(sourceClass), true);
......
}
2.3 @Import
注解的解析
2.3.1 processImports
方法
已经知道@Import
注解在那个方法里面进行解析了。先看看对应的方法参数的含义。
参数 | 含义 |
---|---|
ConfigurationClass configClass | 当前需要解析的Configuration类 |
SourceClass currentSourceClass | 类的源数据封装对象 |
Collection<SourceClass> importCandidates | Configuration类引入的其他类,以及其他类中引入的别的类,比如A引入B,B引入C,C引入D。那么这个就包含了B,C,D |
boolean checkForCircularImports | 是否检查循环引入 |
现在对方法内部进行分析
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
//检查包含有Import注解的集合是不是空的,空的则表示没有
if (importCandidates.isEmpty()) {
return;
}
//检查是否存在循环引入,通过deque的方式来检查
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
//将当前bean放到importStack中,用于检查循环引入
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
//import指定的Bean是ImportSelector类型
if (candidate.isAssignable(ImportSelector.class)) {
//实例化指定的ImportSelector子类
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
//如果是ImportSelector的子类DeferredImportSelector的子类则按照DeferredImportSelectorHandler逻辑进行处理
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {//如果不是则获取指定的需要引入的class的ClassNames
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
//根据ClassNames获取并封装成一个SourceClass
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
//继续调用processImports处理
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}//如果是ImportBeanDefinitionRegistrar的子类
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
//如果对应的ImportBeanDefinitionRegistrar子类对象,并放到configClass,这个是用来注册额外的bean的
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {//不是上面任何类的子类就可以进行处理了,将指定的需要引入的bean转化为ConfigurationClass,然后到processConfigurationClass方法中处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
主要的步骤如下:
- 检查解析出来的Import注解集合是不是空的,空的则不处理,直接返回
- 如果设置了需要检查循环依赖,则进行循环依赖的检查,不需要则跳过,进入3步骤
- 进行处理
(1) 将当前bean放到importStack
中,用于检查循环引入
(2)循环处理以下情况
1)如果是ImportSelector
类型的子类。先实例化这个类,然后检查这个类是不是DeferredImportSelector
子类,是的则调用ConfigurationClassParser
的内部类DeferredImportSelectorHandler
的handle
方法处理。如果不是DeferredImportSelector
子类则继续调用processImports
方法处理。按照步骤1从头开始。
2)如果是ImportBeanDefinitionRegistrar
类型的子类,则实例化这个类,然后放到当前解析的bean的importBeanDefinitionRegistrars
属性中,后面注册bean时候调用。
3)没有实现以上任何接口,则将当前的被引入的bean加入到这个bean的importedBy
属性中,然后调用processConfigurationClass
方法。
对上面的信息进行总结。这个方法作用就是解析时候将被用@Import
注解引入的bean加入到使用@Import
注解标签的bean的importedBy
中后面进行解析时候用,还有就是后面注册bean的时候可能也会调用实现了ImportBeanDefinitionRegistrar
类型的子类。
2.3.2 processConfigurationClass
方法处理configClass
上面processImports
方法调用之后最后都会返回到processConfigurationClass
。这里介绍以下这个方法的作用。这个方法会对ConfigurationClass
对象(这个对象封装了贴了@Configuration
或者@Bean
注解的对象的信息)进行解析,解析上面的注解。就我们上面提到的@Component
,@PropertySources
,@ComponentScans
等。最后保存起来,后面实例化的时候会用到(后面实例化的时候使用的是CGLIB的方式实例化的),跟其他的bean不一样。
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
......
do {
//处理configClass
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
//保存到configurationClasses中
this.configurationClasses.put(configClass, configClass);
}
到这里@Import
注解的解析基本就完了。剩下的就是介绍ImportSelector
,ImportBeanDefinitionRegistrar
以及DeferredImportSelector
之间的区别了。下篇文章解析。
3.ImportSelector
,ImportBeanDefinitionRegistrar
以及DeferredImportSelector
在上面的processImports
方法中已经讲解了对所有的@Import
注解中value
值为不同指的情况进行解析。有以下的情况:
-
ImportSelector
接口的实现类 -
DeferredImportSelector
接口的实现类 -
ImportBeanDefinitionRegistrar
接口的实现类 - 非以上3中接口的实现类,也就是普通的类
这里主要讲解1到3这三个接口类的区别。
3.1 ImportSelector
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
ImportSelector
接口作用将方法返回的字符串数组作为bean注入到容器中,注意这里的字符串需要是对象的全路径名称比如A.class.getName()
这种。后面会讲spring的扩展的时候会使用的。
3.2 ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar
这个接口作用是,用户可以实现了之后来自定义来注册需要注册的bean。可以设置自定义的BeanNameGenerator
bean名称生成规则。
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
3.3 DeferredImportSelector
ImportBeanDefinitionRegistrar
是ImportSelector
的子接口,在4.0版本加入的。在这个类里面添加分组的功能,能够将多个DeferredImportSelector
进行分组。同一个组内的ImportBeanDefinitionRegistrar
能够通过实现@Order
注解去实现排序。在调用的时候处理的时候会先按照序号进行排序,然后依次调用对应实现的 ImportSelector
接口的selectImports
方法。
还有一点就是DeferredImportSelector
的调用逻辑在,所有的@Configuration
已经解析了之后在调用的。这点可以在2.2.4 ConfigurationClassParser的解析过程
代码中看出来。
public interface DeferredImportSelector extends ImportSelector {
default Class<? extends Group> getImportGroup() {
return null;
}
interface Group {
......
}
这里将区别列举出来
类 | 作用 |
---|---|
ImportSelector |
将方法返回的字符串数组作为bean注入到容器中 |
ImportBeanDefinitionRegistrar |
自定义来注册bean |
DeferredImportSelector |
跟ImportSelector 差不多只不过多了分组的功能,处理在@Configuration 类型bean解析之后 |