第五章 spring-connet之Imports注解来龙去脉

会有一股神秘感。 提交于 2019-12-07 17:40:06

前言

imports是一个在spring体系里非常重要的注解,基本每个Enable开头的注解必然有一个import注解。接下来我们深入研究下import的作用。看小节的同学建议先取看PostProcessorRegistrationDelegate与BeanFactoryPostProcessor体系AnnotationConfigUtils

PS: 可以先看这个博客了解下Spring Import 三种用法与源码解读

解析

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

	// 这里是从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,完成想要的功能。

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不会立即执行,会保存到一个集合中,一起执行

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解析完成

只有一次出发机会

ConfigurationClassPostProcessor对象只会被执行一次,这次只会解析那些内容。请看下面的代码,只会解析UserConsumeApplication.class的所有注解,所以有一些imports注解是不会被扫描,执行到的。自己也被坑过一次,以为自己的imports会被扫描并且执行,搞了好久,真坑啊。

@SpringBootApplication
public class UserConsumeApplication {

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

#######

坐在一个很小的公园的花坛上写完。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!