> 文档中心 > SpringBoot自动装配实现原理(从SpringBoot启动开始)

SpringBoot自动装配实现原理(从SpringBoot启动开始)

目录

  • 前言
  • 一、SpringBoot启动类
    • 1、@SpringBootApplication注解
    • 2、main方法,程序入口,执行SpringApplication的静态方法run
  • 二、执行SpringApplication实例化后对象的run方法
  • 三、根据当前应用类型创建对应的Spring容器
    • 1、 应用类型webApplicationType
    • 2、Spring容器的实例化
    • 3、AnnotatedBeanDefinitionReader的实例化
    • 4、将ConfigurationClassPostProcessor注册到BeanDefinitionRegistry中
  • 四、容器刷新,调用Spring框架的refresh方法
    • 1、invokeBeanFactoryPostProcessors方法,执行BeanFactory后置处理
    • 2、执行processConfigBeanDefinitions处理方法
    • 3、ConfigurationClassParser解析自动配置类的过程
    • 4、AutoConfigurationImportSelector内部类AutoConfigurationGroup的process方法的执行
  • 总结

前言

SpringBoot框架是目前企业级Java开发热门框架,亦是微服务项目的基础框架。SpringBoot因其约定优于配置以及强大的可扩展性,使程序员者开发起来简单,扩展起来方便。本文章主要介绍从SpringBoot启动开始,一步一步实现自动装配原理的过程。

一、SpringBoot启动类

@SpringBootApplicationpublic 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并执行一些操作。
SpringBoot自动装配实现原理(从SpringBoot启动开始)

2、main方法,程序入口,执行SpringApplication的静态方法run

main方法为java程序执行入口,SpringBoot项目也不例外,因此,探索自动装配实现的原理,从SpringBoot的main方法开始。由上述可以代码看到其调用了SpringApplication类的静态方法run,并将启动类的class对象以及参数传递下去。我们点进去看方法的调用过程,可以知道其实际会实例化一个SpringApplication对象并执行这个对象的run方法,如下所示。
SpringBoot自动装配实现原理(从SpringBoot启动开始)


二、执行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);//重点方法1context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);//重点方法2refreshContext(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)的创建。
SpringBoot自动装配实现原理(从SpringBoot启动开始)

从上面代码可以看到,此方法调用了SpringApplication成员变量applicationContextFactory的create方法,并传入当前应用类型webApplicationType。applicationContextFactory对象具体是什么呢?来看下下面的代码。
SpringBoot自动装配实现原理(从SpringBoot启动开始)

根据上面代码可以看到ApplicationContextFactory是一个函数式接口,其唯一抽象方法为create(WebApplicationType webApplicationType) .
而ApplicationContextFactory中一个ApplicationContextFactory的实现DEFAULT,其会从spring.factories文件中获取ApplicationContextFactory对应的value,即ApplicationContextFactory的实现,然后调用此实现create方法,从而创建得到Spring容器对象。
SpringBoot自动装配实现原理(从SpringBoot启动开始)

从上图可以看到,其中有两个实现类AnnotationConfigReactiveWebServerApplicationContext以及AnnotationConfigServletWebServerApplicationContext。
SpringBoot自动装配实现原理(从SpringBoot启动开始)

AnnotationConfigServletWebServerApplicationContext的create方法会根据传入的webApplicationType与SERVLET进行对比,若不同则返回null,若相同则返回AnnotationConfigServletWebServerApplicationContext实例化对象,也即创建并返回了一个Spring容器对象。

3、AnnotatedBeanDefinitionReader的实例化

在上述第2点的Spring容器即AnnotationConfigServletWebServerApplicationContext实例化过程中,会进行AnnotatedBeanDefinitionReader的实例化,并将当前AnnotationConfigServletWebServerApplicationContext对象做为参数传入。
SpringBoot自动装配实现原理(从SpringBoot启动开始)
SpringBoot自动装配实现原理(从SpringBoot启动开始)

AnnotationConfigServletWebServerApplicationContext实现了BeanDefinitionRegistry接口
SpringBoot自动装配实现原理(从SpringBoot启动开始)

4、将ConfigurationClassPostProcessor注册到BeanDefinitionRegistry中

通过第3点可以看到,在AnnotatedBeanDefinitionReader的实例化过程中会调用AnnotationConfigUtils的registerAnnotationConfigProcessors方法,其会将ConfigurationClassPostProcessor添加到当前BeanDefinitionRegistry中。ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor接口,因此,可以看出ConfigurationClassPostProcessor实际上是一个BeanFactory的后置处理器/增强器,其会在Spring容器刷新过程中,执行相对应的方法。
SpringBoot自动装配实现原理(从SpringBoot启动开始)

从上面代码可以看到,首先会判断当前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后置处理

@Overridepublic 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相应的扩展操作。
SpringBoot自动装配实现原理(从SpringBoot启动开始)
invokeBeanFactoryPostProcessors方法会委托PostProcessorRegistrationDelagate进行处理,会将Spring容器对象beanFactory传递下去,下面为执行重点方法。

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {   //...省略部分会根据参数获取beanFactoryPostProcessors//下面代码会获取到前面注册在Spring容器中的ConfigurationClassPostProcessorList<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);//循环遍历for (String ppName : postProcessorNames) {//ConfigurationClassPostProcessor实现了PriorityOrderedif (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);//执行BeanDefinitionRegistryPostProcessorsinvokeBeanDefinitionRegistryPostProcessors(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处理方法

SpringBoot自动装配实现原理(从SpringBoot启动开始)
代码跟进来,会发现此处调用了其processConfigBeanDefinitions方法,如下。
SpringBoot自动装配实现原理(从SpringBoot启动开始)

processConfigBeanDefinitions方法会调用ConfigurationClassParser的parse方法进行注解解析,并执行相关ImportSelector实现的方法将需要注册的配置类添加到一个Set集合中;之后进行获取并构造BeanDefinition注册到Register中。

3、ConfigurationClassParser解析自动配置类的过程

上面讲到processConfigBeanDefinitions中会调用ConfigurationClass解析器的解析方法parse,此方法是如何进行解析并添加到解析器中的CofigurationClasses集合中的,其通过判断最终都会调用到解析器中的processConfigurationClass方法,其会doProcessConfigurationClass方法,在doProcessConfigurationClass方法中会有一个processImports方法,此方法就是用于递归解析@Import注解,并获取到注解上面的class对象,执行相关方法,其会通过deferredImportSelectorHandler.handle方法,会调用到实现了DeferredImportSelector.Group接口的process方法。
SpringBoot自动装配实现原理(从SpringBoot启动开始)
SpringBoot自动装配实现原理(从SpringBoot启动开始)

4、AutoConfigurationImportSelector内部类AutoConfigurationGroup的process方法的执行

上面我们讲过@EnableAutoConfiguration注解上的@Import注解的value为AutoConfigurationImportSelector,其有一个内部类AutoConfigurationGroup,它实现了DeferredImportSelector.Group,当前ConfigurationClassParser解析到此时,便会执行其重写的process方法,如下。
SpringBoot自动装配实现原理(从SpringBoot启动开始)

从上面代码的调用过程getAutoConfigurationEntry——>getCandidateConfigurations——>SpringFactoriesLoader.loadFactoryNames,到此处我们可以看到其最终通过spring.factories文件中EnableAutoConfiguration作为key值找到的所有全类限定名,并添加到集合中,便可提供给上层方法进行获取,最后创建对应的BeanDefinition对象,在后面Bean的实例化过程中,便可实现自动装配了。

当然,在SpringBoot2.7.0版本中除了读取spring.factories文件外,其还提供了另外一种读取模式,其会从META-INF/spring路径下查找*.imports文件,也可以获取到配置类的全类名,如下代码。
SpringBoot自动装配实现原理(从SpringBoot启动开始)
SpringBoot自动装配实现原理(从SpringBoot启动开始)

总结

下面为SpringBoot自动装配实现的简要流程图,实际远不止这些,有兴趣的可以研究一下整个SpringBoot启动过程源码。
SpringBoot自动装配实现原理(从SpringBoot启动开始)