Sping源码(七)—ConfigurationClassPostProcessor ——@Import解析

序言

本篇文章主要介绍@Import注解的解析,稍有不同的是我们将要在SpringBoot的启动流程中介绍@Impot注解的原理,并连带着将SpringBoot的自动装配原理也稍加介绍。

SpringBootApplication

这里我是创建了2.3.12版本的SpringBoot,没有额外配置,直接看启动类注解@SpringBootApplication即可。

启动类
我们都知道启动类中核心注解@SpringBootApplication里一共包含3个重要的注解,分别是@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

@SpringBootApplication
public class BootdemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootdemoApplication.class, args);
    }
}
// 省略部分注解...
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	//省略部分源码...
}

EnableAutoConfiguration
而我们需要着重关注的是@EnableAutoConfiguration注解。其中@Import注解在解析是会引入AutoConfigurationImportSelector.class

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

AutoConfigurationPackage
@AutoConfigurationPackage中也包含@Import注解,会引入AutoConfigurationPackages.Registrar.class,简单介绍一下,不用太关注。

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

流程图
在这里插入图片描述

run()

主方法运行前先简单回顾一下我们是如何一步一步创建的ConfigurationClassPostProcessor类。

@ComponentScan注解、context:component-scan解析 ——> 注册BeanDefinition(ConfigurationClassPostProcessor impl BDRPP) ——> invokeBeanFactoryPostProcessors方法 (因为ConfigurationClassPostProcessor 实现了BDRPP类) ——> postProcessBeanDefinitionRegistry(处理@Import、@Bean、@Component等注解)。

run()
回顾完流程,看下SpringBoot的run()是如何调用的。

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

省略中间步骤,可以看到run()方法中调用了很多方法,而调用的众多方法中有一个 this.refreshContext(context); 方法。点进去会发现这个方法就是我们Spring中调用的主流程refresh()方法。

 public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, listeners);
            throw new IllegalStateException(var9);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var8) {
            this.handleRunFailure(context, var8, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var8);
        }
    }

invokeBeanFactoryPostProcessors()
主流程的调用步骤不再这里过多赘述,我们直接看invokeBeanFactoryPostProcessors()方法,打上断点可以看到获取BeanDefinitionRegistryPostProcessor类型的类中已经包含了ConfigurationClassPostProcessor。
在这里插入图片描述

@Import

代码继续向下执行,解析完@Component、@PropertySources等注解后来到了@Import注解的解析。

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		//如果是@Component注解,则优先递归处理内部类
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		//获取BeanDefinition中包含ComponentScan注解的集合
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		//1.componentScans不为null
		//2.根据@Conditional注解判断是否应该跳过
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				//解析@ComponentScan注解
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					//如果根据@ComponentScan注解获取到的beanDefinition中有@Configuration、@Import、@Bean等注解,则递归处理
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		//@Import注解解析
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		//省略部分代码。
}

getImports
看getImports()方法之前先看看参数sourceCalss是什么。
我们是获取到所有符合条件的BeanDefinition后进行遍历,而我们的启动类的注解嵌套中包含@Configuration等注解,所以一定会获取到启动类。
在这里插入图片描述
当前sourceClass为启动类BootdemoApplication,而getImports()会递归处理,收集类中带有@Import注解类的并获取其@Import中value属性值。

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		//创建集合,用来存放包含@Import注解的类
		Set<SourceClass> imports = new LinkedHashSet<>();
		//创建集合,用来存放处理过的类(防止无限递归)
		Set<SourceClass> visited = new LinkedHashSet<>();
		collectImports(sourceClass, imports, visited);
		return imports;
	}

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {
		//如果该类没有被处理过,则进行处理
		if (visited.add(sourceClass)) {
			//遍历该类上的所有注解
			for (SourceClass annotation : sourceClass.getAnnotations()) {
				//获取注解类名
				String annName = annotation.getMetadata().getClassName();
				//如果注解类名不是@Import,则继续递归遍历该类上的注解
				if (!annName.equals(Import.class.getName())) {
					collectImports(annotation, imports, visited);
				}
			}
			//获取该类上的@Import注解的value属性值
			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
	}

处理完成后,SpringBootApplication启动类中包含@Import注解的类共两个,所以imports集合也有两个元素,刚好对应了我们上面流程图中所画的包含@Import的类。
在这里插入图片描述

processImports

接着向下看处理@Import的主流程方法。

流程图
在这里插入图片描述
源码
在流程图和源码中可以看到在处理@Import的过程中,会根据类的归属不同而有不同的逻辑,在我们getImport方法中添加到imports集合的两个类分别属于**ImportSelector(AutoConfigurationImportSelector.class)**和 ImportBeanDefinitionRegistrar (AutoConfigurationPackages.Registrar.class)

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

		if (importCandidates.isEmpty()) {
			return;
		}
		//如果该类已经存在于导入栈(importStack)中,则说明存在循环导入
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			//压入importStack,防止循环导入
			this.importStack.push(configClass);
			try {
				//遍历每一个@Import注解
				for (SourceClass candidate : importCandidates) {
					//如果是ImportSelector的实现类
					if (candidate.isAssignable(ImportSelector.class)) {
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						// 如果候选的类是一个ImportSelector , 则委托它来确定是否导入
						Class<?> candidateClass = candidate.loadClass();
						//反射实例化ImportSelector
						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
								this.environment, this.resourceLoader, this.registry);
						//获取ImportSelector的排除过滤器
						Predicate<String> selectorFilter = selector.getExclusionFilter();
						if (selectorFilter != null) {
							exclusionFilter = exclusionFilter.or(selectorFilter);
						}
						//如果是DeferredImportSelector的实现类,放入延迟导入处理器中(预留到所有配置类加载完成后统一处理)
						if (selector instanceof DeferredImportSelector) {
							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的实现类
					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
						ImportBeanDefinitionRegistrar registrar =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						//将ImportBeanDefinitionRegistrar和当前类添加到ConfigurationClass中
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {
						// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						// 如果候选的类既不是ImportSelector也不是ImportBeanDefinitionRegistrar,则将其作为@Configuration类进行处理
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						/**
						 * 如果Import的类型是普通类,则将其当作带有@Configuration的类一样处理
						 * 将candidate构造为ConfigurationClass,标注为importedBy,意味着它是通过被@Import进来的
						 * 后面处理会用到这个判断将这个普通类注册进DefaultListableBeanFactory
						 */
						processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
					}
				}
			}
			// 省略catch部分
			finally {
				this.importStack.pop();
			}
		}
	}

上面的方法中只是获取到了@Import注解中对应value,按照类的实现进行了分类,但并没有对具体的类做任何的处理。
这里我们重点关注 AutoConfigurationImportSelector 类,因为是 DeferredImportSelector 的实现类,所以会添加到deferredImportSelectorHandler集合中。

类图关系
在这里插入图片描述

process

添加到deferredImportSelectorHandler中的Import什么时候处理?

parse()方法的底层调用的是doProcessConfigurationClass(),就是我们处理@Component、@Bean、@Import的主方法,所以在此处的调用就是在所有配置类加载完成后统一处理。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				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部分
		}

		this.deferredImportSelectorHandler.process();
	}

process
process方法的底层就是SpringBoot自动装配的原理。
顺着 process —》 processGroupImports —》getImports —》 process —》getAutoConfigurationEntry —》getCandidateConfigurations方法的执行顺序看下来,会发现结尾会显式的调用getCandidateConfigurations方法。
所以我们直接看getCandidateConfigurations方法源码。

public void process() {
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					deferredImports.forEach(handler::register);
					handler.processGroupImports();
				}
			}
			finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}
	public void processGroupImports() {
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
				grouping.getImports().forEach(entry -> {
				//省略部分源码....
				});
			}
		}
public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}
	public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
		
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(annotationMetadata);
			//省略部分源码....
		}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//省略部分源码...
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

		//省略部分源码
	}
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());

		return configurations;
	}

自动装配

getSpringFactoriesLoaderFactoryClass
将EnableAutoConfiguration.class作为参数传入loadFactoryNames方法中。

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

loadSpringFactories
加载META-INF/spring.factories路径文件并放入缓存中。

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		//如果缓存中存在,则直接返回
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			//url:META-INF/spring.factories
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				//将Url转换成Resource便于加载
				UrlResource resource = new UrlResource(url);
				//加载properties
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				//遍历properties放入缓存中
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
	}

loadFactoryNames
将参数EnableAutoConfiguration.class作为key,从缓存中获取,如果不存在则返回一个默认的空LIst。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

我们来看一看spring.factories文件中都有什么
在这里插入图片描述
所以通过EnableAutoConfiguration.class从缓存中获取到的就是要自动装配时加载的类。
此处先卖个关子,更详细的流程等到SpringBoot篇章再展开。接下来我们总结一下整体的流程。

总结

流程图
在这里插入图片描述

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值