Spring Boot源码探索——自动配置的内部实现

前面写了两篇文章 《Spring Boot自动配置的魔法是怎么实现的》《Spring Boot起步依赖:定制starter》,分别分析了Spring Boot的自动配置和起步依赖。在感慨Spring Boot的方便之余,也不禁产生了一点疑惑,Spring Boot 内部究竟是怎么触发自动配置和起步依赖的?

先看下面这段代码,我们只需要调用SpringApplication.run()方法就能启动整个Spring应用。

@SpringBootApplication
public class DemoApplication {

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

非常简单的代码,要了解Spring Boot运行原理,我们先从这个方法开始:

	public ConfigurableApplicationContext run(String... args) {
		...
		...
		try {
			...
			...
			
			// 看到refresh很容易想到AbstractApplicationContext的refresh()方法
			refreshContext(context);
			
			...
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
        
        ...
	}

run()方法里面有很多方法逻辑,我们不必每个都去深究,对于了解Spring的同学来说,看到refreshContext(context);很容易想到AbstractApplicationContext的refresh()方法。AbstractApplicationContext的refresh()方法正是Spring 核心功能的实现逻辑。我们来看看refreshContext()方法的代码,会发现里面有一个refresh()方法:

	private void refreshContext(ConfigurableApplicationContext context) {
	    // 这个方法更类似了
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

继续到refresh()方法中去:

	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}

在这里我们看到应用上下文applicationContext被强转为AbstractApplicationContext,从而去调用AbstractApplicationContext的refresh()方法。AbstractApplicationContext的refresh()方法会做很多事情:

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 准备刷新的上下文环境
		prepareRefresh();
		
		// 初始化BeanFactory
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		
		// 对BeanFactory进行各种功能填充
		prepareBeanFactory(beanFactory);

		try {
			// 子类覆盖方法做额外的处理
			postProcessBeanFactory(beanFactory);

			// 激活各种BeanFactory处理器
			invokeBeanFactoryPostProcessors(beanFactory);

			// 注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用是在getBean时
			registerBeanPostProcessors(beanFactory);

			// 国际化处理
			initMessageSource();

			// 初始化应用消息广播器,并放入“applicationEventMulticaster”Bean中
			initApplicationEventMulticaster();

			// 留给子类来初始化其它的Bean
			onRefresh();

			// 在所有注册的Bean中查找Listener Bean,并注册消息广播器中
			registerListeners();

			// 初始化单例Bean(非延迟初始化的)
			finishBeanFactoryInitialization(beanFactory);

			// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知其它人
			finishRefresh();
		}
        ...
	}
}

我们主要关注invokeBeanFactoryPostProcessors(beanFactory)方法,因为这个方法激活各种BeanFactory处理器,包括BeanDefinitionRegistryPostProcessor。BeanDefinitionRegistryPostProcessor可以在Spring 处理Bean的定义之前,注册Bean的定义。现在的项目基本都用Java Config的方式来配置Bean,这些Bean就是通过BeanDefinitionRegistryPostProcessor注册到Spring中的。

invokeBeanFactoryPostProcessors(beanFactory)方法中的代码挺多的,一开始可能根本找不到我们想要的代码。我们想知道Spring Boot是如何实现自动配置的,自动配置根本上来说,是根据判断条件来加载Bean。现在找到了Spring加载Bean的代码,但是目前我们在这个方法里根本看不到任何与Spring Boot有关的逻辑,只知道Spring Boot确实调用了这个方法。既然Spring Boot调用了这个方法,尽管情况不明,我们也可以试着debug一下。我直接把断点打在了invokeBeanFactoryPostProcessors()方法中:

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
	Set<String> processedBeans = new HashSet<>();

	if (beanFactory instanceof BeanDefinitionRegistry) {
	
	    ... 
	    ...
	    
	    // Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let the bean factory post-processors apply to them!
		// Separate between BeanDefinitionRegistryPostProcessors that implement
		// PriorityOrdered, Ordered, and the rest.
		List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

		// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
	    
	    ...
	    
	}
    
    ...
}

上面这段代码会从Spring的BeanFactory中寻找BeanDefinitionRegistryPostProcessor类型的Bean,如果找到了就加载它。我们为什么要注意这段代码呢?前面说过,BeanDefinitionRegistryPostProcessor可以向Spring注册Bean定义,那么Spring Boot自动配置的那些Bean也应该是由一个我们现在不清楚的BeanDefinitionRegistryPostProcessor来注册的。如果你正在调试的SpringBoot应用没有配置任何额外东西的话,那么这里的调试信息,应该和下面的图相似:

我们看到一个 ConfigurationClassPostProcessor 的类,阅读这个类的文档可以知道,正是这个类处理 @Confiugration 注解的类。正好,@SpringBootApplication 间接继承 @Confiugration。显然,ConfigurationClassPostProcessor 将会对Spring Boot的配置进行处理。我们进一步去 ConfigurationClassPostProcessor 中查看:

/**
 * Derive further bean definitions from the configuration classes in the registry.
 */
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	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);
	}
	this.registriesPostProcessed.add(registryId);

	processConfigBeanDefinitions()方法(registry);
}

ConfigurationClassPostProcessor 中的 postProcessBeanDefinitionRegistry() 这个方法,是在上面的 AbstractApplicationContext 类中被调用。再继续看 processConfigBeanDefinitions() 方法:

/**
 * Build and validate a configuration model based on the registry of
 * {@link Configuration} classes.
 */
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    ...
    ...
    // Parse each @Configuration class
	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);

	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
	Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
	do {
		parser.parse(candidates);
		parser.validate();
        
        ...
        
    }while (!candidates.isEmpty());
    
    ...
}

又是一个比较复杂的方法,不过好在源码中的注释表明 ConfigurationClassParser 类负责进行 @Configuration 类的解析。我们直接看 ConfigurationClassParser 类的 parse() 方法:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
	this.deferredImportSelectors = new LinkedList<>();

	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 (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
		}
	}

	processDeferredImportSelectors();
}

这个方法主要有两个逻辑,parse()方法对BeanDefinition进行解析,递归找出所有的 @Configuration 注解的类。接着processDeferredImportSelectors()处理 DeferredImportSelector 类。这个 DeferredImportSelector 类看源码文档注释:

A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans have been processed. This type of selector can be particularly useful when the selected imports are {@code @Conditional}.

意思就是说,这个类能够处理 @Conditional 类型的配置。恰好,我们知道,Spring Boot就是依赖 @Conditional 实现的自动配置。我们应该是找对了地方。

回过头来看 Spring Boot 的 @EnableAutoConfiguration 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	...
	...
}

我们看到了 @Import(AutoConfigurationImportSelector.class) 注解,其中它导入的 AutoConfigurationImportSelector 就是 DeferredImportSelector 接口的实现类。这也就意味着,AutoConfigurationImportSelector 是Spring Boot 自动配置的一个关键类。我们继续看看 AutoConfigurationImportSelector 类的实现:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
			.loadMetadata(this.beanClassLoader);
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	List<String> configurations = getCandidateConfigurations(annotationMetadata,
			attributes);
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	// filter()方法是关键
	configurations = filter(configurations, autoConfigurationMetadata);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return StringUtils.toStringArray(configurations);
}

很自然地,我们肯定先看从 ImportSelector 继承的方法 selectImports(),在这个方法里面,从字面意思的角度,我们肯定也会先看filter()方法:

private List<String> filter(List<String> configurations,
		AutoConfigurationMetadata autoConfigurationMetadata) {
	long startTime = System.nanoTime();
	String[] candidates = StringUtils.toStringArray(configurations);
	boolean[] skip = new boolean[candidates.length];
	boolean skipped = false;
	for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
		invokeAwareMethods(filter);
		boolean[] match = filter.match(candidates, autoConfigurationMetadata);
		for (int i = 0; i < match.length; i++) {
			if (!match[i]) {
				skip[i] = true;
				skipped = true;
			}
		}
	}
    ...
    ...
}

这里我们看到一个 AutoConfigurationImportFilter 类,程序获取这个类,并调用它的 match()方法,这让我们联想到 Condition 类的 macthes() 方法。我们查看这个类的继承体系,会发现它是 OnClassCondition 类的父类。而 OnClassCondition 负责处理 @ConditionalOnClass@ConditionalOnMissingClass 两个注解。

到这里,Spring Boot 自动配置的流程大致是清楚了。用一张程序调用栈的图片或许能够表达清楚:

这里我们只发现了 OnClassCondition ,实际上Spring Boot中还有很多其他的 Conditon,比如:OnBeanConditionOnPropertyConditionOnResourceCondition等等。他们基本上也是类似的调用流程。

原文地址:https://www.cnblogs.com/bluemilk/p/10612879.html