从@Import的源码解读Springboot的自动装配

1. 1@Import源码解析入口

ConfigurationClassParser.java
parse(){
processConfigurationClass()

// 处理selectImport 类,这里面自动装配的东西
this.deferredImportSelectorHandler.process();
}

ConfigurationClassParser.java
//开始循环解析包含指定注解的集合对象中的数据
	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		// 循环遍历configCandidates
		for (BeanDefinitionHolder holder : configCandidates) {
			// 获取BeanDefinition
			BeanDefinition bd = holder.getBeanDefinition();
			// 根据BeanDefinition类型的不同,调用parse不同的重载方法,实际上最终都是调用processConfigurationClass()方法
			try {
				// 注解类型
				if (bd instanceof AnnotatedBeanDefinition) { 
				// 注意,调用同名方法parse()->processConfigurationClass()->doProcessConfigurationClass(位置一)
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				// 有class对象的 。抽象类
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
    //上面的parse,只是解析Componet等属性,
		// 对于@Import的解析,普通的对象,直接进bd放到configurationClasses中,this.reader.loadBeanDefinitions(configClasses);
		// 对于实现ImportSelector接口的,放到deferredImportSelectorHandler列表中,此处开始回到处理了。
		// 获取所有的配置,遍历,调用处理import注解的方法,如果是普通的类,直接放进configurationClasses(键是对应上面configClasses)中
	         
       //此处开始处理handler中的class ,加载springboot中的自动装配的配置
		this.deferredImportSelectorHandler.process();
}	

接上面位置一处。

  processConfigurationClass(){//开始解析各种注解。里面有@Component注解等,启动注意@Import注解
   //位置二
  sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);

// 把解析到的结果保存起来。注意:如果Import是普通类,那么这里能保存上
this.configurationClasses.put(configClass, configClass);
}
1.2 解析@Import注解

承接上面的位置二
doProcessConfigurationClass{
// 处理 @Import注解。查找所有的@import注解,找到注解中的类并实例化。直接处理或者加入handler中
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {

		// 如果使用@Import注解修饰的类集合为空,那么直接返回
		if (importCandidates.isEmpty()) {
			return;
		}
		....
			try {
				// 遍历每一个@Import注解的类
				for (SourceClass candidate : importCandidates) {
					// 检验配置类Import引入的类是否是ImportSelector子类
					if (candidate.isAssignable(ImportSelector.class)) {
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						// 候选类是一个导入选择器->委托来确定是否进行导入
						Class<?> candidateClass = candidate.loadClass();
						// 通过反射生成一个ImportSelect对象
						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
								this.environment, this.resourceLoader, this.registry);
						// 获取选择器的额外过滤器
						Predicate<String> selectorFilter = selector.getExclusionFilter();
						if (selectorFilter != null) {
							exclusionFilter = exclusionFilter.or(selectorFilter);
						}
						// 判断引用选择器是否是DeferredImportSelector接口的实例
						// 如果是则应用选择器将会在所有的配置类都加载完毕后加载
						if (selector instanceof DeferredImportSelector) {
							// 重点核心-用来实现自动装配。。。。。将选择器添加到deferredImportSelectorHandler实例中,预留到所有的配置类加载完成后统一处理自动化配置类
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {
							// 获取引入的类,然后使用递归方式将这些类中同样添加了@Import注解引用的类
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
							// 重点。递归处理。被Import进来的类也有可能@Import注解
							processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
						}
					}
					// 如果是实现了ImportBeanDefinitionRegistrar接口的bd
					else  if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// Candidate class is an ImportBeanDefinitionRegistrar ->
						// delegate to it to register additional bean definitions
						// 候选类是ImportBeanDefinitionRegistrar  -> 委托给当前注册器注册其他bean
 						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						/** * 放到当前configClass的importBeanDefinitionRegistrars中;在ConfigurationClassPostProcessor处理configClass时会随之一起处理 */
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {			
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());							
//说明仅仅是一个普通的类,还是解析它,其实里面什么都没处理,但是把它加入到configurationClasses中了,然后加载bd就有了
				processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
					}
				}
			}
		....
		}
	}

其中SpringBoot中的自动装配类AutoConfigurationImportSelector implements DeferredImportSelector,就是实现了DeferredImportSelector接口。
经过上面的步骤,说有的@Import标记的处理完成。
=》 // 回调,处理@Import 放进去的选择器 ,SpringBoot的自动装配
this.deferredImportSelectorHandler.process();

->handler.processGroupImports();
-->     // 理解的重点
        // getImports 获取到所有要装备的类,然后再遍历处理**
				grouping.getImports().forEach{
				//循环遍历,再调用自动装备的类,可能自动装配的类还有@Import注解
				// 更为重要的是,解析该类的时候,如果是普通类,那么调用processConfigurationClass处理一遍,最后把
				// bean信息放到this.configurationClasses.put(configClass, configClass);中去了。那么接下来就能放到容器的bdMaps中,等待实例化
				processImports(...)		
}
--->
		public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
	//调用process 方法了,这里过度到SpringBoot		
		this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}
==============================SpringBoot===============================
AutoConfigurationImportSelector#process(){
	->getAutoConfigurationEntry(..)
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		//获取所有的自动装配的配置。本例获取到130个
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
        //排除掉不符合条件的
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
        //本例还有34个
		return new AutoConfigurationEntry(configurations, exclusions);
	}

在这里插入图片描述获取到到配置文件中的配置
在这里插入图片描述

2. @Import为什么需要用@Component或衍生注解标记
2.1 xml

加上@Component注解,

public class TestSelfTag {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user=(User)context.getBean("user");
    }
}
 <context:component-scan base-package="com.songbl.selftag" >
    </context:component-scan>
@Component
@Import(User.class)
public class MyImportTestBean {
}

xml形式启动的时候,加上这个注解,才能doScan 扫描进去,放bdMaps中。
在ConfigurationClassPostProcessor 解析的时候,获取所有的已经注册的bd
String[] candidateNames = registry.getBeanDefinitionNames(); ,如果不加这个注解,那么没放进去,
获取不到,就没法解析了,@Import就不生效了。

2.1 注解
 public static void main(String[] args) throws NoSuchMethodException {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
       //注册bd到容器
        ac.register(MyTestConfiguration.class);
        ac.refresh();
    }
@Component    
@ComponentScan(basePackages="com.songbl.selftag")
public class MyTestConfiguration { }

对于注解启动的,调用register方法注册了bean信息,后面获取的时候,

	//获得上面注册MyTestConfiguration的bd
	String[] candidateNames = registry.getBeanDefinitionNames();

   if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
	// 添加到对应的集合对象中(带注解的放进去);本例MyTestConfiguration 被@ComponentScan(basePackages="com.songbl.selftag")
     configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));// 然后parse(上面准备的列表configCandidates),递归处理,本例到这里开始处理@ComponentScan(basePackages="com.songbl.selftag")了

在这里插入图片描述

总结:想让生效,就要用@Component 或者衍生注解 标记,使得扫描进去,才有机会被处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值