第五章 spring-connet之Imports註解前因後果

前言

imports是一個在spring體系裏很是重要的註解,基本每一個Enable開頭的註解必然有一個import註解。接下來咱們深刻研究下import的做用。看小節的同窗建議先取看PostProcessorRegistrationDelegate與BeanFactoryPostProcessor體系AnnotationConfigUtilsjava

PS: 能夠先看這個博客瞭解下Spring Import 三種用法與源碼解讀web

解析

ConfigurationClassParser的processImports方法是最核心的方法spring

// 這裏是從SourceClass得到imports註解,注意jdk8容許標記多個註解,一個class能夠標記多個不一樣註解,其餘註解上也能夠標記imports註解,因此須要一個set存放
	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<>();
		Set<SourceClass> visited = new LinkedHashSet<>();
		collectImports(sourceClass, imports, visited);
		return imports;
	}
	
	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates,boolean checkForCircularImports) throws IOException {

		if (importCandidates.isEmpty()) {
			return;
		}

		if (checkForCircularImports &amp;&amp; 
this.importStack.contains(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, 
this.importStack));
		}
		else {
			this.importStack.push(configClass);
			try {
				for (SourceClass candidate : importCandidates) {
					// 看註解裏面的類是否是ImportSelector的實現類
					if (candidate.isAssignable(ImportSelector.class)) {
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
						invokeAwareMethods(selector);
						// 判斷是不是須要延遲加載,繼承DeferredImportSelector類的都會延遲加載
						if (this.deferredImportSelectors != null &amp;&amp; selector instanceof DeferredImportSelector) {
							this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
						}
						else {
							// 執行ImportSelector的實現類,返回須要加載的內容
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// 這個是很是重要的,ImportBeanDefinitionRegistrar.addImportBeanDefinitionRegistrar方法的形參是registrar。
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =BeanUtils.instantiateClass(candidateClass,ImportBeanDefinitionRegistrar.class);
						invokeAwareMethods(registrar);
						configClass.addImportBeanDefinitionRegistrar(registrar, 
currentSourceClass.getMetadata());
					}
					else {
						// 
若是都不是,就調動processConfigurationClass(迭代了,processImports上兩級的調用就是
processConfigurationClass)
						this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Exception ex) {
				throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				this.importStack.pop();
			}
		}
	}

重點

ImportSelector

看ImportSelector.selectImports 就知道返回一個字符串數組,字符串數組是class的徹底限定名,進行processImports掃描數組

public interface ImportSelector {
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}


String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection&lt;SourceClass&gt; importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar的重要行比ImportSelector高,最重要的是形參 BeanDefinitionRegistry,從下面代碼BeanPostProcessorsRegistrar對象能夠看 到,ImportBeanDefinitionRegistrar具有向容器裏面註冊bean的能力。能夠添加想要的bean,完成想要的功能。ide

public interface ImportBeanDefinitionRegistrar {
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

		private ConfigurableListableBeanFactory beanFactory;

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws 
BeansException {
			if (beanFactory instanceof ConfigurableListableBeanFactory) {
				this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
			}
		}

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
	if (this.beanFactory == null) {
				return;
	}
			 registerSyntheticBeanIfMissing(registry,"webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry,"errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class);
		}

		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,String name, Class&lt;?&gt; beanClass) {
			if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}

	}

獲得ImportBeanDefinitionRegistrar不會當即執行,會保存到一個集合中,一塊兒執行this

ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, 
ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

public void  addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
		this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}

執行時機,是等待因此初始化的註解或者class解析完成 .net

只有一次出發機會

ConfigurationClassPostProcessor對象只會被執行一次,此次只會解析那些內容。請看下面的代碼,只會解析UserConsumeApplication.class的全部註解,因此有一些imports註解是不會被掃描,執行到的。本身也被坑過一次,覺得本身的imports會被掃描而且執行,搞了很久,真坑啊。code

@SpringBootApplication
public class UserConsumeApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserConsumeApplication.class, args);
    }
}

#######對象

坐在一個很小的公園的花壇上寫完。blog

相關文章
相關標籤/搜索