spring源码解析——@Import注解解析与ImportSelector,ImportBeanDefinitionRegistrar以及DeferredImportSelector区别

  • Post author:
  • Post category:其他




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

方法。会有两种处理方式:

  1. 当前的

    beanFactory



    BeanDefinitionRegistry

    类型的

    (1) 先处理

    beanFactory

    ,然后按照是否实现了

    PriorityOrdered

    ,

    Ordered

    以及两个都没有实现的顺序来处理

    BeanDefinitionRegistryPostProcessor

    接口的实现类。

    (2)处理实现了

    BeanFactoryPostProcessor

    接口的子类依次调用实现的

    postProcessBeanFactory

    方法
  2. 当前的

    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);
			......
			}
			......
}

两个解析的位置分别是:

  1. 第一个位置是

    ConfigurationClassParser

    类的

    parse

    方法,解析能够直接从

    beanFactory

    中获取的候选bean。
  2. 第二个是

    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 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();
			}
		}
	}

主要的步骤如下:

  1. 检查解析出来的Import注解集合是不是空的,空的则不处理,直接返回
  2. 如果设置了需要检查循环依赖,则进行循环依赖的检查,不需要则跳过,进入3步骤
  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

值为不同指的情况进行解析。有以下的情况:


  1. ImportSelector

    接口的实现类

  2. DeferredImportSelector

    接口的实现类

  3. ImportBeanDefinitionRegistrar

    接口的实现类
  4. 非以上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解析之后



版权声明:本文为szhlcy原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。