目录
前言
SpringBoot框架是目前企业级Java开发热门框架,亦是微服务项目的基础框架。SpringBoot因其约定优于配置以及强大的可扩展性,使程序员者开发起来简单,扩展起来方便。本文章主要介绍从SpringBoot启动开始,一步一步实现自动装配原理的过程。
一、SpringBoot启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
1、@SpringBootApplication注解
@SpringBootApplication注解其下有@EnableAutoConfiguration,而@EnableAutoConfiguration下面有@Import注解,且@Import的value为AutoConfigurationImportSelector.class,这是重点,SpringBoot在启动过程中会对启动类上的注解进行递归获取,拿到AutoConfigurationImportSelector并执行一些操作。
2、main方法,程序入口,执行SpringApplication的静态方法run
main方法为java程序执行入口,SpringBoot项目也不例外,因此,探索自动装配实现的原理,从SpringBoot的main方法开始。由上述可以代码看到其调用了SpringApplication类的静态方法run,并将启动类的class对象以及参数传递下去。我们点进去看方法的调用过程,可以知道其实际会实例化一个SpringApplication对象并执行这个对象的run方法,如下所示。
二、执行SpringApplication实例化后对象的run方法
由第一步我们可以看到,run方法执行链会去创建一个SpringApplication对象,在其构造器中执行应用类型的判断、初始化、监听器的获取以及判断主启动类等方法。实例化完成之后,便调用本对象的run方法,代码如下。
//SpringBoot2.7.0版本
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//重点方法1
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//重点方法2
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
在整个自动装配实现过程中,主要原理涉及到了createApplicationContext()方法以及refreshContext(context)的调用,其他方法在本文中暂时不展开,有兴趣的可以跟一下源码。
三、根据当前应用类型创建对应的Spring容器
1、 应用类型webApplicationType
当前SpringBoot版本中一个共有三种webApplicationType(web应用类型):NONE、SERVLET、REACTIVE。实际开发应用经常使用的SERVLET,而REACTIVE(响应式web)也会根据需求进行选用,本文以SERVLET应用为例进行讲解。
webApplicationType的获取在第一步中SpringApplication对象实例化的过程中会通过WebApplicationType的静态方法deduceFromClasspath方法的调用来获取当前应用的类型,其会通过当前项目中是否存在某个类来区分类型,具体细节可以看一下deduceFromClasspath方法的源码。
2、Spring容器的实例化
根据第二步run方法中的createApplicationContext方法,讲解一下Spring容器(ConfigurableApplicationContext)的创建。
从上面代码可以看到,此方法调用了SpringApplication成员变量applicationContextFactory的create方法,并传入当前应用类型webApplicationType。applicationContextFactory对象具体是什么呢?来看下下面的代码。
根据上面代码可以看到ApplicationContextFactory是一个函数式接口,其唯一抽象方法为create(WebApplicationType webApplicationType) .
而ApplicationContextFactory中一个ApplicationContextFactory的实现DEFAULT,其会从spring.factories文件中获取ApplicationContextFactory对应的value,即ApplicationContextFactory的实现,然后调用此实现create方法,从而创建得到Spring容器对象。
从上图可以看到,其中有两个实现类AnnotationConfigReactiveWebServerApplicationContext以及AnnotationConfigServletWebServerApplicationContext。
AnnotationConfigServletWebServerApplicationContext的create方法会根据传入的webApplicationType与SERVLET进行对比,若不同则返回null,若相同则返回AnnotationConfigServletWebServerApplicationContext实例化对象,也即创建并返回了一个Spring容器对象。
3、AnnotatedBeanDefinitionReader的实例化
在上述第2点的Spring容器即AnnotationConfigServletWebServerApplicationContext实例化过程中,会进行AnnotatedBeanDefinitionReader的实例化,并将当前AnnotationConfigServletWebServerApplicationContext对象做为参数传入。
AnnotationConfigServletWebServerApplicationContext实现了BeanDefinitionRegistry接口
4、将ConfigurationClassPostProcessor注册到BeanDefinitionRegistry中
通过第3点可以看到,在AnnotatedBeanDefinitionReader的实例化过程中会调用AnnotationConfigUtils的registerAnnotationConfigProcessors方法,其会将ConfigurationClassPostProcessor添加到当前BeanDefinitionRegistry中。ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor接口,因此,可以看出ConfigurationClassPostProcessor实际上是一个BeanFactory的后置处理器/增强器,其会在Spring容器刷新过程中,执行相对应的方法。
从上面代码可以看到,首先会判断当前BeanDefinitionRegistry中是否包含该Bean名称对应的bean定义信息,如果没有的话,则会创建一个beanName为org.springframework.context.annotation.internalConfigurationAnnotationProcessor,class对象为ConfigurationClassPostProccessor的BeanDefinition,并添加到当前的BeanDefinitionRegistry中,在后面方法的执行中,就可以取出此ConfigurationClassPostProccessor并进行相应的方法操作了。
四、容器刷新,调用Spring框架的refresh方法
上面讲完了Spring容器的创建,接下来将通过Spring框架源码中的refresh方法,分析其是如何一步一步执行并实现自动装配的。
SpringBoot在run方法中执行refreshContext方法,其实际上调用了AbstractApplicationContext的refresh方法。
1、invokeBeanFactoryPostProcessors方法,执行BeanFactory后置处理
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
//此方法为重点方法,会获取当前容器中实现了BeanFactoryPostProccessor接口的BeanDefinition并执行相应方法,实现BeanFactory后置处理,我们也可以自己自定义BeanFactoryPostProccesor的实现类,来完成扩展需求。
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
上面讲过在Spring容器创建的过程中,将ConfigurationClassPostProccessor注册在当前容器中,而ConfigurationClassPostProcessors是BeanDefinitionRegistryPostProcessor接口实现,BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor接口。因此,其中的invokeBeanFactoryPostProcessors的方法便能够去执行到ConfigurationClassPostProcessor相应的扩展操作。
invokeBeanFactoryPostProcessors方法会委托PostProcessorRegistrationDelagate进行处理,会将Spring容器对象beanFactory传递下去,下面为执行重点方法。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
//...省略部分会根据参数获取beanFactoryPostProcessors
//下面代码会获取到前面注册在Spring容器中的ConfigurationClassPostProcessor
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
//循环遍历
for (String ppName : postProcessorNames) {
//ConfigurationClassPostProcessor实现了PriorityOrdered
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
//执行BeanDefinitionRegistryPostProcessors
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
//省略...
}
//执行BeanDefinitionRegistryPostProcessors:
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
.tag("postProcessor", postProcessor::toString);
//循环执行BeanDefinitionRegistryPostProcessors实现的postProcessBeanDefinitionRegistry方法,其中便会执行到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法
postProcessor.postProcessBeanDefinitionRegistry(registry);
postProcessBeanDefRegistry.end();
}
}
由上面代码可以看出,通过遍历所有的BeanDefinitionRegistryPostProcessors实现类,并分别执行postProcessBeanDefinitionRegistry方法,其便会执行到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法。
2、执行processConfigBeanDefinitions处理方法
代码跟进来,会发现此处调用了其processConfigBeanDefinitions方法,如下。
processConfigBeanDefinitions方法会调用ConfigurationClassParser的parse方法进行注解解析,并执行相关ImportSelector实现的方法将需要注册的配置类添加到一个Set集合中;之后进行获取并构造BeanDefinition注册到Register中。
3、ConfigurationClassParser解析自动配置类的过程
上面讲到processConfigBeanDefinitions中会调用ConfigurationClass解析器的解析方法parse,此方法是如何进行解析并添加到解析器中的CofigurationClasses集合中的,其通过判断最终都会调用到解析器中的processConfigurationClass方法,其会doProcessConfigurationClass方法,在doProcessConfigurationClass方法中会有一个processImports方法,此方法就是用于递归解析@Import注解,并获取到注解上面的class对象,执行相关方法,其会通过deferredImportSelectorHandler.handle方法,会调用到实现了DeferredImportSelector.Group接口的process方法。
4、AutoConfigurationImportSelector内部类AutoConfigurationGroup的process方法的执行
上面我们讲过@EnableAutoConfiguration注解上的@Import注解的value为AutoConfigurationImportSelector,其有一个内部类AutoConfigurationGroup,它实现了DeferredImportSelector.Group,当前ConfigurationClassParser解析到此时,便会执行其重写的process方法,如下。
从上面代码的调用过程getAutoConfigurationEntry——>getCandidateConfigurations——>SpringFactoriesLoader.loadFactoryNames,到此处我们可以看到其最终通过spring.factories文件中EnableAutoConfiguration作为key值找到的所有全类限定名,并添加到集合中,便可提供给上层方法进行获取,最后创建对应的BeanDefinition对象,在后面Bean的实例化过程中,便可实现自动装配了。
当然,在SpringBoot2.7.0版本中除了读取spring.factories文件外,其还提供了另外一种读取模式,其会从META-INF/spring路径下查找*.imports文件,也可以获取到配置类的全类名,如下代码。
总结
下面为SpringBoot自动装配实现的简要流程图,实际远不止这些,有兴趣的可以研究一下整个SpringBoot启动过程源码。