SpringBoot托管配置:
- Springboot实际上通过约定大于配置的方式,使用 xx-starter统一的对Bean进行默认初始化,用户只需要很少的配置就可以进行开发了。
SpringBoot做了什么:简化Spring应用的创建、运行、调试、部署。约定大于配置。
- starter快速整合常用的第三方依赖:原理是通过Maven子父工程的方式。
- 全部采用注解形式:简化XML配置。
- 内置Http服务器
启动流程
SpringApplication是springboot驱动spring应用上下文的引导类,他的run()方法启动Spring应用,实质上是为Spring应用创建并初始化Spring上下文:
\1. 初始化监听器,以及添加到SpringApplication的自定义监听器。
\2. 发布ApplicationStartedEvent事件,如果想监听ApplicationStartedEvent事件,你可以这样定义:public class ApplicationStartedListener implements ApplicationListener,然后通过SpringApplication.addListener(..)添加进去即可。
\3. 装配参数和环境,确定是web环境还是非web环境。
\4. 装配完环境后,就触发ApplicationEnvironmentPreparedEvent事件。
\5. 如果SpringApplication的showBanner属性被设置为true,则打印启动的Banner。
\6. 创建ApplicationContext,会根据是否是web环境,来决定创建什么类型的ApplicationContext。
\7. 装配Context的环境变量,注册Initializers、beanNameGenerator等。
\8. 发布ApplicationPreparedEvent事件。
\9. 注册springApplicationArguments、springBootBanner,加载资源等
\10. 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
\11. 调用ApplicationContext的refresh()方法,装配context beanfactory等非常重要的核心组件。
\12. 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
\13. 发布ApplicationReadyEvent事件,启动完毕,表示服务已经可以开始正常提供服务了。通常我们这里会监听这个事件来打印一些监控性质的日志,表示应用正常启动了。
SpringBoot会触发其他的一些事件,这些事件按下列顺序触发:
(1)ApplicationStartingEvent:项目刚启动时触发,此时除了注册监听器和初始器之外,其他所有处理都没有开始;
(2)ApplicationEnvironmentPreparedEvent:上下文得到环境信息之后触发,此时上下文创建还没有创建;
(3)ApplicationPreparedEvent:bean的定义信息加载完成之后触发,此时bean还没有初始化;
(4)ApplicationReadyEvent:在所有bean初始化完毕,所有回调处理完成,系统准备处理服务请求时触发;
(5)ApplicationFailedEvent:启动过程出现异常时候触发。
SpringApplication.run()
如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:
\1. 根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
\2. 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
\3. 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
\4. 推断并设置main方法的定义类。
它会执行以下步骤:
\1. 创建一个合适的ApplicationContext实例 (取决于classpath)。
\2. 注册一个CommandLinePropertySource,以便将命令行参数作为Spring properties。
\3. 刷新application context,加载所有单例beans。
\4. 激活所有CommandLineRunner beans。
SpringApplication详解(run执行启动)
SpringApplication是springboot驱动spring应用上下文的引导类,他的run()方法启动Spring应用,实质上是为Spring应用创建并初始化Spring上下文。
执行流程
\1. 初始化监听器,以及添加到SpringApplication的自定义监听器。
\2. 发布ApplicationStartedEvent事件,如果想监听ApplicationStartedEvent事件,你可以这样定义:public class ApplicationStartedListener implements ApplicationListener,然后通过SpringApplication.addListener(..)添加进去即可。
\3. 装配参数和环境,确定是web环境还是非web环境。
\4. 装配完环境后,就触发ApplicationEnvironmentPreparedEvent事件。
\5. 如果SpringApplication的showBanner属性被设置为true,则打印启动的Banner。
\6. 创建ApplicationContext,会根据是否是web环境,来决定创建什么类型的ApplicationContext。
\7. 装配Context的环境变量,注册Initializers、beanNameGenerator等。
\8. 发布ApplicationPreparedEvent事件。
\9. 注册springApplicationArguments、springBootBanner,加载资源等
\10. 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
\11. 调用ApplicationContext的refresh()方法,装配context beanfactory等非常重要的核心组件。
\12. 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
\13. 发布ApplicationReadyEvent事件,启动完毕,表示服务已经可以开始正常提供服务了。通常我们这里会监听这个事件来打印一些监控性质的日志,表示应用正常启动了。
SpringBoot会触发其他的一些事件,这些事件按下列顺序触发:
(1)ApplicationStartingEvent:项目刚启动时触发,此时除了注册监听器和初始器之外,其他所有处理都没有开始;
(2)ApplicationEnvironmentPreparedEvent:上下文得到环境信息之后触发,此时上下文创建还没有创建;
(3)ApplicationPreparedEvent:bean的定义信息加载完成之后触发,此时bean还没有初始化;
(4)ApplicationReadyEvent:在所有bean初始化完毕,所有回调处理完成,系统准备处理服务请求时触发;
(5)ApplicationFailedEvent:启动过程出现异常时候触发。
SpringApplication.run()
如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:
\1. 根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
\2. 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
\3. 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
\4. 推断并设置main方法的定义类。
它会执行以下步骤:
\1. 创建一个合适的ApplicationContext实例 (取决于classpath)。
\2. 注册一个CommandLinePropertySource,以便将命令行参数作为Spring properties。
\3. 刷新application context,加载所有单例beans。
\4. 激活所有CommandLineRunner beans。
Run启动方式
\1. SpringApplication.run()的底层其实就是new了一个SpringApplication的对象,并执行run()方法。
\2. 有时我们需要创建多层次的ApplicationContext (例如,父子关系的Spring的ApplicationContext 和SpringMVC),这时我们可以使用SpringApplicationBuilder讲多个方法调用串起来,通过parent() 和 child()来创建多层次的ApplicationContext。
启动信息如下:
第 9 行,启动SampleController。
第10行,查找active profile,无,设为default。
第11行,刷新上下文。
第12行,初始化tomcat,设置端口8080,设置访问方式为http。
第13行,启动tomcat服务。
第14行,启动Servlet引擎。
第15行,Spring内嵌的WebApplicationContext 初始化开始。
第16行,Spring内嵌的WebApplicationContext 初始化完成。
第17行,映射servlet,将 dispatcherServlet 映射到 [/] 。
第18行,映射filter,将 characterEncodingFilter 映射到 [/*] 。
第19行,映射filter,将 hiddenHttpMethodFilter 映射到 [/*] 。
第20行,映射filter,将 httpPutFormContentFilter 映射到 [/*] 。
第21行,映射filter,将 requestContextFilter 映射到 [/*] 。
第22行,查找 @ControllerAdvice。
第23行,映射路径 “{[/]}” 到 cn.larry.spring.controller.SampleController.home()。
第24行,映射路径 “{[/error]}” 到 org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)。
第25行,映射路径 “{[/error],produces=[text/html]}” 到 org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)。
第30行,tomcat启动完毕。
第31行,SampleController启动耗费的时间。
第32行,初始化 dispatcherServlet 。
第33行,dispatcherServlet 的初始化已启动。
第34行,dispatcherServlet 的初始化已完成。
第35行,收到shutdown关闭请求。
第36行,关闭AnnotationConfigEmbeddedWebApplicationContext。
SpringBootApplication的启动过程非常复杂,只需要在main方法里调用SpringApplicatio.run()方法即可启动Spring Boot应用。
自动配置原理
整体描述自动配置整个过程:
- Spring Boot 通过@EnableAutoConfiguration 注解开启自动配置
- 加载 spring.factories 中注册的各种 AutoConfiguration 类
- 当某个 AutoConfiguration类满足其注解@Conditional 指定的生效条件(例如Starters 提供的依赖、配置或 Spring 容器中是否存在某个 Bean 等)时,实例化该 AutoConfiguration 类中定义的 Bean(组件等),并注入 Spring 容器。
各个概念:
- @EnableAutoConfiguration:该注解由组合注解
@SpringBootApplication
引入,完成自动配置开启,扫描各个jar包下的spring.factories文件,并加载文件中注册的AutoConfiguration类等。 - spring.factories:配置文件,位于 jar 包的 META-INF 目录下,按照指定格式注册了自动配置的 AutoConfiguration 类。spring.factories 也可以包含其他类型待注册的类。该配置文件不仅存在于 Spring Boot 项目中,也可以存在于自定义的自动配置(或 Starter)项目中。
- AutoConfiguration 类:自动配置类,代表了 Spring Boot 中一类以 XXAutoConfiguration命名的自动配置类。其中定义了三方组件集成 Spring 所需初始化的 Bean 和条件。
- @Conditional:条件注解及其衍生注解,在 AutoConfiguration 类上使用,当满足该条件注解时才会实例化 AutoConfiguration 类。
- Starters:三方组件的依赖及配置,Spring Boot 已经预置的组件。Spring Boot 默认的Starters 项目往往只包含了一个 pom 依赖的项目。如果是自定义的 starter,该项目还需包含 spring.factories 文件、AutoConfiguration 类和其他配置类。
@SpringBootApplication 注解的功能
在 Spring Boot 入口类 XXXApplication
中,唯一的一个注解就是@SpringBootApplication
。这个注解也没什么特别的,它是通过该注解内组合的@EnableAutoConfiguration
开启自动配置的。
注解中组合了 @SpringBootConfiguration
、@EnableAutoConfiguration
和 @ComponentScan
。因此,在实践过程中也可以使用这 3 个注解来替代@SpringBootApplication。
该注解提供了以下成员属性(注解中的成员变量以方法的形式体现):
- exclude:根据类(Class) 排除指定的自动配置,该成员属性覆盖了@SpringBoot-Application中组合的@ EnableAutoConfiguration 中定义的 exclude 成员属性。
- excludeName :根据类名排除指定的自动配置,覆盖了@ EnableAutoConfiguration 中的excludeName 的成员属性。
- :scanBasePackages:指定扫描的基础 package,用于激活@Component 等注解类的初始化。
- scanBasePackageClasses:扫描指定的类,用于组件的初始化。
- proxyBeanMethods:指定是否代理@Bean 方法以强制执行 bean 的生命周期行为。此功能需要通过运行时生成 CGLIB 子类来实现方法拦截。该子类有一定的限制,比如配置类及其方法不允许声明为 final 等。
proxyBeanMethods 的默认值为 true,允许配置类中进行 inter-beanreferences (bean 之 间的引用)以及对该配置的@Bean 方法的外部调用。如果@Bean 方法都是自包含的,并且仅提供了容器使用的普通工程方法的功能,则可设置为 false,避免处理 CGLIB 子类。SpringBoot 2.2 版本上市后新增该成员属性,后面章节涉及的自动配置类中基本都会用到proxyBeanMethods,一 般情况下都配置为 false。
通过以上源代码我们会发现,Spring Boot 中大量使用了*@AliasFor 注解,该注解用于桥接到其他注解*,该注解的属性中指定了所桥接的注解类。如果点进去查看,会发现@SpringBootApplication 定义的属性在其他注解中已经定义过了。之所以使用@AliasFor注解并重新在@SpringBootApplication 中定义,更多是为了减少用户使用多注解带来的麻烦。
@EnableAutoConfiguration注解的功能
在未使用 Spring Boot 的情况下,Bean 的生命周期由 Spring 来管理,然而 Spring 无法自动配置@Configuration 注解的类。所以用@EnableAutoConfiguration 注解来允许Spring Boot 根据约定自动管理@Configuration 注解标注的类。
@EnableAutoConfiguration借助@Import的支持,收集和注册依赖包中相关的bean定义。@Import,给容器导入一个Registrar组件
- @Configuration&、@Bean这两个bean两个注解一起使用就可以创建一个基于java代码的配置类,可以用来替代相应的xml配置文件。
- @Configuration注解的类可以看作是能生产让Spring IoC容器管理的Bean实例的工厂。
- @Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册到spring容器中。
自动配置原理
@SpringBootApplication
@EnableAutoConfiguration
@Import
(AutoConfigurationImportSelector.class):*AutoConfigurationImportSelector.class
会默认加载MATE-INF/spring.factories
,以及好多自动配置类*@AutoConfigurationPackage
@Import
(AutoConfigurationPackages.Registrar.class):*AutoConfigurationPackages.Registrar.class
会去加载启动类所在包下面的所有类*
@SpringBootConfiguration
(本质上是@Configuration
注解,随意包装改名了一下)
@EnableAutoConfiguration -》
Springboot 底层实现自动装配的步骤是:
- Springboot 应用启动;
@SpringBootApplication
起作用;@EnableAutoConfiguration
;@AutoConfigurationPackage:
这个组合注解主要是@Import(AutoConfigurationPackages.Registrar.class)
, 它通过将Registrar
类导入到容器中,而Registrar
类作用是扫描主配置类同级目录及其子包,并将相应的组件导入到 Springboot创建管理容器中@Import(AutoConfigurationImportSelector.class):
它通过将AutoConfigurationImportSelector
类导入到容器中,AutoConfigurationImportSelector
类作用是通过selectImports
方法执行的过程中,会使用内部工具类SpringFactoriesLoader
查找classpath
上所用 jar 包中的MATE-INF/spring.factories
进行加载, 实现将配置类信息交给SpringFactory
加载器进行一系列的容器创建过程。
参考:
启动流程
- 创建 ApplicationArguments 对象 初始化默认应用参数类。args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:–server.port=9000
- 项目运行环境Environment的预配置:并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,并过滤掉不要的env。
- 创建Spring容器:根据 webApplicationType 进行判断,确定容器类型。
- Spring容器前置处理:容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
- 刷新容器:开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化。
- Spring容器后置处理:扩展接口,设计模式中的模板方法,默认为空实现。afterRefresh(context, applicationArguments);
- 发出结束执行的事件通知:listeners.started(context);
- 执行Runners :用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
- 发布应用上下文就绪事件
详细流程:
public ConfigurableApplicationContext run(String... args) {
// 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置 headless 属性
configureHeadlessProperty();
//(1)获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 创建 ApplicationArguments 对象 初始化默认应用参数类
// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//(2)项目运行环境Environment的预配置
// 创建并配置当前SpringBoot应用将要使用的Environment
// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 排除不需要的运行环境
configureIgnoreBeanInfo(environment);
// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
Banner printedBanner = printBanner(environment);
// (3)创建Spring容器
// 根据 webApplicationType 进行判断,确定容器类型,如果为 SERVLET 类型,会反射创建相应的字节码,AnnotationConfigServletWebServerApplicationContext, 接着使用之前传世话设置的context、environment、listeners、applicationArgument 进行应用上下文的组装配置
context = createApplicationContext();
// 获得异常报告器 SpringBootExceptionReporter 数组
//这一步的逻辑和实例化初始化器和监听器的一样,
// 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// (4)Spring容器前置处理
//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// (5):刷新容器
// 开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等),同时向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭
refreshContext(context);
// (6):Spring容器后置处理
//扩展接口,设计模式中的模板方法,默认为空实现。
// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
afterRefresh(context, applicationArguments);
// 停止 StopWatch 统计时长
stopWatch.stop();
// 打印 Spring Boot 启动的时长日志。
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// (7)发出结束执行的事件通知
listeners.started(context);
// (8):执行Runners
//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
// (9)发布应用上下文就绪事件
//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
// 这样整个Spring Boot项目就正式启动完成了。
try {
listeners.running(context);
} catch (Throwable ex) {
// 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回容器
return context;
}
内嵌Tomcat原理
spring boot是指出多种服务器启动的,并不只是tomcat,还有jetty等。
spring boot的servlet web服务器的配置类位于
spring-boot-autoconfigure.jar
下的/META-INF/spring.factories
文件中的一个名为ServletWebServerFactoryAutoConfiguration
的自动配置类。在该自动配置类中,会通过@Import向容器中注入四个配置类。EmbeddedTomcat
配置类又会向Spring容器注入TomcatServletWebServerFactory
,这个类就是Tomcat启动的关键类,用于创建TomcatWebServer。此外,
ServletWebServerFactoryAutoConfiguration
中还会注入一系列的Customizer
,用于修改内嵌Tomcat的参数和配置在Spring的
refresh()
方法中,有一个onRefresh()
方法。默认是一个空的实现,这是留给子类容器实现的扩展方法。在所有的bean定义被注入到容器中之后调用onRefresh()
方法。之后,则会对所有的普通单例bean进行实例化和初始化。默认的web服务容器是
AnnotationConfigServletWebServerApplicationContext
,它又继承了ServletWebServerApplicationContext
,该类就对onRefresh
方法进行了实现,并且Spring boot的web服务器就是在此启动的!onRefresh()
方法内部调用了createWebServer()
方法创建web服务器。createWebServer方法的代码,会通过之前配置的ServletWebServerFactory,获取webServer,即创建web服务器。
总体逻辑:
spring-boot-autoconfigure.jar
下的/META-INF/spring.factories
文件引入ServletWebServerFactoryAutoConfiguration
自动配置类。ServletWebServerFactoryAutoConfiguration
自动配置类引入ServletWebServerFactoryAutoConfiguration.EmbeddedTomcat
配置类EmbeddedTomcat
配置类向Spring容器注入TomcatServletWebServerFactory
- Spring执行
refresh()
方法,bean定义注入容器中后,执行onRefresh()
方法(空方法,由子类实现),之后执行普通单例bean的实例化和初始化。 - web容器继承类
ServletWebServerApplicationContext
执行onRefresh()
方法,调用到了createWebServer()
方法来创建web服务器。 createWebServer()
方法通过之前配置的ServletWebServerFactory
,获取webServer,即创建web服务器。
转载请注明来源