Spring Boot启动详解
- 从启动类开始
- 核心注解@SpringBootApplication
- SpringBoot启动原理
- 1)SpringApplication实例初始化及设置
- 2)执行run方法的逻辑
- 3)创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource 以及Profile)。
- 4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法
- 5) 如果SpringApplication的showBanner属性被设置为true,则打印banner
- 6) 创建ApplicationContext
- 7)SpringApplication在prepareContext方法中对上下文对象进行预配置
- 8)刷新应用上下文对象
- 9) 发布应用程序已启动(ApplicationStartedEvent)事件
- 10) 在BeanFactory中获取所有ApplicationRunner和CommandLineRunner并调用他们的run方法
- 11) 异常处理,如果run方法的处理过程中发生异常,则对exitCode进行相应处理
- SpringBoot流程图
从启动类开始
@SpringBootApplication
public class SpringdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringdemoApplication.class, args);
}
}
核心注解@SpringBootApplication
@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 表明子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启Springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { //扫描路径设置
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
三个Annotation:
- @Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
...
}
- @EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
- @ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
...
}
所以,如果我们使用如下的SpringBoot启动类,整个SpringBoot应用依然可以与之前的启动类功能对等
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class SpringdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringdemoApplication.class, args);
}
}
每次都写三个比较繁琐,所以写一个@SpringBootApplication方便
三个注解
@Configuration
- 是JavaConfig形式的Spring Ioc容器的配置类使用 的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动 类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。
- 举几个简单例子回顾下, XML跟config配置方式的区别:
表达形式层面 - 基于XML配置的方式
<?xml version="1.0"encoding="UTF‐8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring‐beans‐3.0.xsd" default‐lazy‐init="true">
<!‐‐bean定义‐‐>
</beans>
- 基于JavaConfig的配置方式是这样
@Configuration public class MockConfiguration{
//bean 定义
}
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类
- 注册bean定义层面,基于XML的配置形式是这样
<bean id="mockService" class="..MockServiceImpl">
...
</bean>
- 基于JavaConfig的配置形式是这样
@Configuration
public class MockConfiguration{
@Bean
public MockService mockService(){
return new MockServiceImpl();
}
}
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id
- 表达依赖注入关系层面,为了表达bean与bean之间的依赖关系,在XML形式中一般如下
<bean id="mockService" class="..MockServiceImpl">
<propery name ="dependencyService" ref="dependencyService" />
</bean>
<bean id="dependencyService" class="DependencyServiceImpl"></bean>
- 基于JavaConfig的配置形式如下
@Configuration public class MockConfiguration{
@Bean
public MockService mockService(){
return new MockServiceImpl(dependencyService());
}
@Bean
public DependencyService dependencyService(){
return new DependencyServiceImpl();
}
}
如果一个bean的定义依赖其他bean,则直接调用对应的额JavaConfig类中依赖bean的创建方法就可以了
@ComponentScan
- @ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素
- @ComponentScan 的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者 bean定义,最终将这些bean定义加载到IoC容器中。
- 我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
- 注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages
@EnableAutoConfiguration
- Spring框架提供的各种名字为@Enable开头的Annotation定义,比如@EnableScheduling、 @EnableCaching、@EnableMBeanExport等
- @EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器。
@EnableMBeanExport是通过@Import将JMX相关的bean定义加载到IoC容器。
而@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义 加载到IoC容器,仅此而已!
@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
在以上注解之中,包含两个比较重要的注解
@AutoConfigurationPackage:自动配置包
@Import:导入自动配置的组件
@AutoConfigurationPackage
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
}
其实就是注册了一个Bean定义
new PackageImport(metadata).getPackageName():返回当前主程序类的同级以及子级的包组件
这也就是为什么我们把XxxApplication放在项目的最高级中
@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector
ImportSelector有一个方法为:selectImports
该方法被AutoConfigurationImportSelector类重写
本类其实是去加载public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;外部文件,这个外部文件里面有很多自动配置的类,如下
注解总结
以上注解,最关键的要属 @Import(EnableAutoConfigurationImportSelector.class),借助 EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应 用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。
自动配置的关键SpringFactoriesLoader
借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration才可以实现自动配置功效
SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其
- 主要功能就是从指定的配置文件 META-INF/spring.factories加载配置。
public abstract class SpringFactoriesLoader { //...
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
...
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
...
}
}
配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据 @EnableAutoConfiguration的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的 一组@Configuration类
所以,@EnableAutoConfiguration自动配置的过程就变成了:从classpath中搜寻所有的METAINF/spring.factories配置文件,并将其中 org.springframework.boot.autoconfigure.EnableautoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类, 然后汇总为一个并加载到IoC容器。
SpringBoot启动原理
SpringApplication的run方法的实现是启动原理探寻的起点,该方法的大致流程如下
1)SpringApplication实例初始化及设置
如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个 SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在 SpringApplication实例初始化的时候,它会提前做几件事情:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
1.根据classpath里面是否存在某个特征类 (org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
2.使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
3.使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
4.推断并设置main方法的定义类。
在这里SpringBoot将应用程序分为三种类型:
- Reactive:Spring团队推出的Reactor编程模型的非阻塞异步Web编程框架WebFlux
- Servlet:基于J2EE Servlet API的编程模型,运行在Servlet容器上
- None:非Web应用程序
通过类路径中是否存在WebFlux中的Dispatcherhandler,SpringMVC中的DispatcherServlet、 Servlet、ConfigurableWebApplicationContext来推断Web应用程序类型
2)执行run方法的逻辑
SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行开始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的 SpringApplicationRunListener。调用它们的started()方法,告诉这些 SpringApplicationRunListener,“SpringBoot应用要开始执行咯!”。
3)创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource 以及Profile)。
- Environment是SpringFramework中一个很重要的接口,用于存放应用程序的配置信息
- PropertySource(org.springframework.core.env.PropertySource)是用来将一个对象以键值 对的形式表示,Spring将多种来源的属性键值对转换成PropertySource来表示
这里的做了如下事情:
1.创建Environment对象
- 在getOrCreateEnvironment方法中,会根据之前推断的webApplicationType(web程序类型)创建不同了实现的Environment对象
2.配置Environment对象
- 应用程序如果有命令行参数,则在Environment中添加一个与这个命令行参数相关的 PropertySource
- 根据命令行参数中spring.profiles.active属性配置Environment对象中的activeProfile
3.发布ApplicationEnvironmentPreparedEvent(应用环境已准备)事件
4.将Environment中的spring.main属性绑定到SpringAppilcation对象中
- 在执行到这一步时,Environment中已经包含了用户设置的profile文件属性
5.转换Environment对象的类型
- 在上一步中,如果用户使用spring.main.web-application-type属性手动设置了应用程序的 webApplicationType并且用户设置的类型与SpringApplication推断出来的不一致,则 SpringApplication会将环境对象转换成用户设置的webApplicationType相关的类型
4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法
遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
5) 如果SpringApplication的showBanner属性被设置为true,则打印banner
6) 创建ApplicationContext
根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext(通过 webApplicationType类型判断),最后通过BeanUtils实例化上下文对象,并返回。
7)SpringApplication在prepareContext方法中对上下文对象进行预配置
主要做了如下事情:
1.执行所有ApplicationContextInitializer的initialize方法
- 这些ApplicationContextInitializer是在SpringApplication中的构造函数中加载的(通过读取spring.factories加载)
2.发布ApplicationContextInitializedEvent(上下文已初始化)事件发布3.ApplicationPreparedEvent(上下文已准备)事件
8)刷新应用上下文对象
这里是ApplicationContext真正开始初始化容器和创建bean的阶段,其中bean的整个生命周期可以从这一步骤看出来;Spring Framework中的所有ApplicationContext实现都直接或间接继承自 AbstracttApplicationContext,它的refresh方法描述了整个上下文的初始化逻辑
举例:
以AnnotationConfigServletWebServerApplicationContext(当应用的webApplicationType 为Servlet时使用)这个实现类为例
AbstractApplicationContext的refresh方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1.Prepare this context for refreshing.
prepareRefresh();
// 2.Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3.Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// 4.Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// 5.Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// 6.Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// 7.Initialize message source for this context.
initMessageSource();
// 8.Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 9.Initialize other special beans in specific context subclasses.
onRefresh();
// 10.Check for listener beans and register them.
registerListeners();
// 11.Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// 12.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();
}
}
}
详解如下:
1.准备更新上下文时的预备工作:
- 初始化PropertySource
- 验证Enrivonment中必要的属性
2.获取上下文的内部BeanFactory
- 内部BeanFactory的实现类是DefaultListableBeanFactory
3.对BeanFactory做些预备工作:
- 设置BeanFactory的Bean类加载器、Bean表达式解析器、属性编辑器注册表
- 添加类型为ApplicationContextAwareProcessor、ApplicationListenerDetector的 BeanPostProcessor
- 让BeanFactory在自动装配时忽略一些接口类型
- 注册可解析的依赖(自动装配时碰到这些类型直接注入,包括BeanFactory、 ResourceLoader、ApplicationEventPublisher、ApplicationContext)
- 在BeanFactory中注册一些单例对象,包括environment、systemProperties、 systemEnvironment
4.对BeanFactory进行预处理
- 添加一个WebApplicationContextServletContextAwareProcessor的BeanPostProcessor
- 使BeanFactory自动装配时忽略ServletContextAware接口
- 在BeanFactory中注册request、session两种scope
- 注册可解析的依赖(自动装配时碰到这些类型可以注解注入,包括ServletRequest、 ServletResponse、HttpSession、WebRequest)
5.执行容器中的BeanFactoryPostProcessor执行到这时容器已经注册了三个 BeanFactoryPostProcessor,分别为
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor ApplicationContexttInitializer初始化时注册 ConfigurationWarningsApplicationContextInitializer#ConfigurationWarningsPostProcessor ApplicationContexttInitializer初始化时注册 ConfigFileApplicationListener$PropertySourceOrderingPostProcessor ApplicationPreparedEvent事件发布时由ConfigFileApplicationListener注册
- 在BeanFactory中找到已注册的BeanFactoryPostProcessor,执行其中类型为 BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
- 循环从BeanFactory中获取BeanDefinitionRegistryPostProcessor(从BeanDefinition注册表 中获取,和上一步的来源不一样);有一个 ConfigurationClassPostProcessor(ApplicationContext的构造函数中注册的) ConfigurationClassPostProcessor会执行SpringBoot的自动装配功能,将spring.factories中 类型为EnableAutoConfiguration的类读取成BeanDefinition并过滤掉不满足条件的,然后注册 到BeanFactory中
- 执行他们的postProcessBeanFactory方法对BeanFactory进行后处理
6.注册BeanPostProcessor,Bean生命周期的钩子,允许用户对实例化后的Bean进行操作 - 从BeanFactory中获取所有BeanPostProcessor
- 在BeanFactory中注册一个类型为BeanPostProcessorChecker的BeanPostProcessor
- 将所有BeanPostProcessor按照实现了PriorityOrdered、Ordered、没有实现排序接口的 顺序注册所有BeanPostProcessor到BeanFactory
- 在BeanFactory中注册一个类型为ApplicationListenerDetector的BeanPostProcessor
7.初始化MessageSource(国际化相关)
8.初始化容器事件广播器(用来发布事件)
- 构造了一个SimpleApplicationEventMulticaster当成默认的事件广播器
9.初始化一些特殊的Bean
- 初始化ThemeSource(跟国际化相关的接口)
- 创建WebServer
10.将所有监听器注册到前两步创建的事件广播器中
11.结束BeanFactory的初始化工作(这一步主要用来将所有的单例BeanDefinition实例化)
12.afterRefresh(上下文刷新完毕) 初始LifecycleProcessor(生命周期处理器),向BeanFactory注册一个 DefaultLifecycleProcessor 调用LifecycleProcessor的onrefresh方法(找到所有已注册的SmartLifecycle,根据 isRunning和isAutoStartup的条件判断,执行SmartLifecycle的start方法) 在ServletWebServerApplicationContetxt中,启动了WebServer并发布了 ServletWebServerInitializedEvent事件
9) 发布应用程序已启动(ApplicationStartedEvent)事件
10) 在BeanFactory中获取所有ApplicationRunner和CommandLineRunner并调用他们的run方法
11) 异常处理,如果run方法的处理过程中发生异常,则对exitCode进行相应处理
- shutdownHook简单介绍
– 作用:JVM退出时执行的业务逻辑
– 添加:Runtime.getRuntime().addShutdownHook()
– 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHook) - 什么时候会调用Shutdown Hook
– 程序正常停止 :Reach the end of program、System.exit
– 程序异常退出:NPE、OutOfMemory
– 受到外界影响停止:Ctrl+C、用户注销或者关机
SpringBoot流程图
- AutoConfigurationImportSelector借助spring框架原有的一个工具类SpringFactoriesLoader的支持, @EnableAutoConfiguration可以智能地完成自动配置。
- SpringApplication执行流程
- Spring Boot自动化配置
- Spring Boot启动流程