天天看點

spring源碼解析[email protected]注解解析與ImportSelector,ImportBeanDefinitionRegistrar以及DeferredImportSelector差別

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解析之後

繼續閱讀