前面写了两篇文章 《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,比如:OnBeanCondition、OnPropertyCondition、OnResourceCondition等等。他们基本上也是类似的调用流程。