Spring Boot面试必问:启动流程(自动装配)
文章目录
一、问题归纳
1.1 Spring Boot启动流程
一个SpringBoot程序都有一个主入口,这个主入口就是main方法,而main方法中都会调用SpringBootApplication.run方法,一个快速了解SpringBootApplication启动过程的好方法就是在run方法中打一个断点,然后通过Debug的模式启动工程,逐步跟踪了解SpringBoot源码是如何完成环境准备和启动加载bean的。
查看SpringBootApplication.run方法的源码就可以发现SpringBoot启动的流程主要分为两大阶段:
初始化SpringApplication运行SpringApplication
运行SpringApplication的过程
其中运行SpringApplication的过程又可以细分为以下几个部分:
1)SpringApplicationRunListeners 引用启动监控模块
2)ConfigrableEnvironment配置环境模块和监听:包括创建配置环境、加载属性配置文件和配置监听
3)ConfigrableApplicationContext配置应用上下文:包括配置应用上下文对象、配置基本属性和刷新应用上下文
1.2 初始化SpringApplication
步骤1进行SpringApplication的初始化,配置基本的环境变量、资源、构造器、监听器,初始化阶段的主要作用是为运行SpringApplication实例对象启动环境变量准备以及进行必要的资源构造器的初始化动作,代码如下
public SpringApplication(ResourceLoader resourceLoader, Object... sources){ this.resourceLoader = resourceLoader; initialize(source); } @SupressWarnings({"unchecked","rowtypes"}) private void initialize(Object[] sources){ if(sources != null && sources.length > 0){ this.sources.addAll(Arrays.asList(sources)); } this.WebEnvironment = deduceWebEnvironment; setInitiallizers((Collection) getSpringFactoriesInstances(ApplicationContextInitiallizer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
SpringApplication方法的核心就是this.initialize(sources)初始化方法,SpringApplication通过调用该方法完成初始化工作。deduceWebEnvironment方法用来判断当前应用的环境,该方法通过获取两个类来判断当前环境是否是Web环境。而getSpringFactoriesInstances方法主要用来从spring.factories文件中找出Key为ApplicationContextInitiallizer的类并实例化,然后调用setInitiallizer方法设置到SpringApplication的initiallizer属性中,找到它所有应用的初始化器。接着调用setListener方法设置应用监听器,这个过程可以找到所有应用程序的监听器,然后找到应用启动主类名称。
1.3 运行SpringApplication(SpringApplication自动装配)
SpringBoot正式启动加载过程,包括启动流程监控模块、配置环境加载模块、ApplicationContext容器上下文环境加载模块。refreshContext方法刷新应用上下文并进行自动化配置模块加载,也就是上文提到的SpringFactoriesLoader根据指定classpath加载META-INF/spring.factories文件的配置,实现自动配置核心功能。运行SpringApplication的主要代码如下:
public ConfigurableApplicationContext run(String... args) { ConfigurableApplicationContext context = null; FailureAnalyzer analyzer = null; configureHeadlessProperty(); // 步骤1 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try{ // 步骤2 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); Banner printBanner = printBanner(environment); // 步骤3 context = createApplicationContext(); prepareContext(context, environment, listeners, applicationArguments, printBanner); refreshContext(context); afterRefresh(context, applicationArguments); listeners.finished(context, null); // 省略 return context; } } ``
1.3.1 SpringApplciationRunListener应用启动监控模块
应用启动监控模块对应上述源码中的注释步骤1到步骤2之间的两行代码,它创建了应用的监听器SpringApplicationRunListeners并开始监听,监控模块通过调用getSpringFactoriesInstances私有协议从META-INF/spring.factories文件中取得SpringApplicationRunListener监听器实例。
当前事件监听器SpringApplicationRunListener中只有一个EventPublishingRunlistener广播事件监听器,它的starting方法会封装成SpringApplicatiionEvent事件广播出去,被SpringApplication中配置的listener监听。这一步骤执行完成后也会同时通知SpringBoot其他模块目前监听初始化已经完成,可以开始执行启动方案了。
1.3.2 ConfigurableEnviroment 配置环境模块和监听
对应上述源码注释中的步骤2到步骤3之间的几行代码,下面分解步骤说明:
(1) 创建配置环境,创建应用程序的环境信息。如果是Web程序,创建StandardServletEnvironment;否则创建StandardEnviroment;
(2)加载属性配置文件,将配置文件加入监听器对象中(SpringApplicationRunListeners)。通过configPropertySource方法设置properties配置文件,通过执行configProfies方法设置profiles;
(3)配置监听,发布environmentPrepared事件,及调用ApplicationListener#onApplicationEvent方法,通知SpringBoot应用的environment已经准备完成。
1.3.3 ConfigurableApplicationContext配置应用上下文
对应源码中的步骤3下面的几行代码,下面分解步骤说明:
(1)配置Spring容器应用上下文对象,它的作用是创建Run方法的返回对象ConfigurableApplicationContext(应用配置上下文),此类主要继承了ApplicationLifeCycle、Closeable接口,而ApplicationContext是Spring框架中负责Bean注入容器的主要载体,负责bean加载、配置管理、维护bean之间依赖关系及Bean生命周期管理。
(2)配置基本属性,对应prepareContext方法将listener、environment、banner、applicationArguments等重要组件与Spring容器上下文对象关联。借助SpringFactoriesLoader查找可用的ApplciationContextInitailizer,
它的initialize方法会对创建好的ApplicationContext进行初始化,然后它会调用SpringApplicationRunListener#contextPrepared方法,此时SpringBoot应用的ApplicationContext已经准备就绪,为刷新应用上下文准备好了容器。(3)刷新应用上下文,对应源码中的refreshContext(context)方法将通过工程模式产生应用上下文中所需的bean。实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载、bean的实例化等核心工作。然后调用SpringApplicationRunListener#finish方法告诉SprignBoot应用程序,容器已经完成ApplicationContext装载。
二、知识详解
2.1 Spring Boot核心注解
@SpringBootApplication
注解是Spring Boot的核心注解,它其实是一个组合注解,包括三个注解
@Configuration:被标注的类等于在Spring的XML配置文件中(applicationContext.xml),装配所有bean事务,提供了一个Spring的上下文环境
@EnableAutoConfiguration:SpringBoot根据应用所声明的依赖来对Spring框架进行自动配置,其中包括两个重要注解:
@AutoConfigurationPackage:该注解上有一个@Import({Registrar.class})注解,其中Registrar类的作用是将启动类所在的包下的所有子包组件扫描注入到spring容器中。
因此这就是为什么将controller、service等包放在启动类的同级目录下的原因
@Import({AutoConfigurationImportSelector.class}):其中AutoConfigurationImportSelector类中有一个getCandidateConfigurations()方法,这个方法通过SpringFactoriesLoader.loadFactoryNames()查找位于META-INF/spring.factories文件中的所有自动配置类并加载这些类。
@ComponentScan:组件扫描,自动扫描和装配Bean,扫描SpringApplication的run方法中的ExammanagerApplication.class所在的包路径下的文件,因此将启动类(main)放在跟包路径下。它去寻找带有@Component注解的类,并为其创建bean。
2.2 总结
SpringBoot应用程序的启动流程主要包括初始化SpringApplication和运行SpringApplication两个过程。其中初始化SpringApplication包括配置基本的环境变量、资源、构造器和监听器,为运行SpringApplciation实例对象作准备;而运行SpringApplication实例为应用程序正式启动加载过程,包括SpringApplicationRunListeners
引用启动监控模块、ConfigrableEnvironment配置环境模块和监听及ConfigrableApplicationContext配置应用上下文。当完成刷新应用的上下文和调用SpringApplicationRunListener#contextPrepared方法后表示SpringBoot应用程序已经启动完成。