Spring Boot 2 特性
摘自官网
- Create stand-alone Spring applications
- Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
- Provide opinionated 'starter' dependencies to simplify your build configuration
- Automatically configure Spring and 3rd party libraries whenever possible
- Provide production-ready features such as metrics, health checks and externalized configuration
- Absolutely no code generation and no requirement for XML configuration
前言
目前 spring boot 已被广泛使用,关于 spring boot 的文章更是随处可见。因此,我们就串联琐碎的知识点,简单整理出一条线。使用spring boot 已经有一段时间了,但是有些知识点还是比较模糊。它启动过程都干了什么?如何实现自动配置的?启动 web 应用的时候什么时候启动 tomcat 服务的?发起一个 http 请求会经历什么过程?
启动过程
spring boot 启动过程的源码分析网上有很多,而且比较详细,就不再详细赘述了。有兴趣的同学自行翻阅资料哈~
在 spring boot 启动的时候,会根据工程的环境(主要是根据特定的类名称)确定一个 ConfigurableApplicationContext
的上下文对象,我们就以 AnnotationConfigServletWebServerApplicationContext
做例子。
// 创建ConfigurableApplicationContext,如果是servlet环境会创建AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
// 从spring.factories中获取SpringBootExceptionReporter工厂,并初始化spring boot的异常分析对象
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新appContext,这里开始启动spring管理bean
refreshContext(context);
// afterRefresh方法是protected的空实现,我们可以用这个钩子做一些事情
afterRefresh(context, applicationArguments);
重点关注 refreshContext
方法,这个方法强制转换对象为 AbstractApplicationContext 后执行他的 refresh
方法
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
经常看 spring 源码的同学,看到这一步应该就比较熟悉了,和我们传统的 SSM 框架一样的启动流程。
启动过程中功能简述
- 通过
SpringFactoriesLoader
提供通用的工厂加载机制,具体是解析工程下所有jar包中的 META-INF/spring.factories 文件,并提供反序列化 bean 功能 - 提供
Properties
和Yaml
配置文件的解析,以及数据绑定 - 推断上下文环境是
SERVLET
、REACTIVE
、NON
, 以创建相应的ConfigurableApplicationContext
来管理 Bean 的生命周期 - 支持可扩展
SPI
接口,包括PropertySourceLoader
、SpringApplicationRunListener
、SpringBootExceptionReporter
、ApplicationContextInitializer
、ApplicationListener
、EnvironmentPostProcessor
、FailureAnalyzer
、FailureAnalysisReporter
等
了解了 SpringFactoriesLoader
实现类似 java spi 的功能,我们就可以自定义实现 SPI
的接口。实现方式:
- 以 java config 形式配置托管,例如:
@Component
- 在执行
run
返回的ConfigurableApplicationContext
中添加(部分接口可以) - 工程中新建 META-INF/spring.factories 文件,以
接口=实现
的形式存放
实现自动配置
我们的 spring boot 的启动类通常会使用 @SpringBootApplication
,该注解包含了@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
,而 @EnableAutoConfiguration
是实现自动配置的关键
...
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
在前面我们提到 spring boot 启动会执行到AbstractApplicationContext.refresh(),之后调用ConfigurationClassPostProcessor.processConfigBeanDefinitions扫描所有的 @Import
,并执行 AutoConfigurationImportSelector
的 selectImports
,该方法调用 SpringFactoriesLoader
读取spring.factories获取 EnableAutoConfiguration
对应的实现类。
方法执行栈
refresh --> invokeBeanFactoryPostProcessors --> ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry --> AutoConfigurationImportSelector.selectImports
最后会在 AbstractApplicationContext.finishBeanFactoryInitialization 初始化非懒加载的对象,完成自动化配置。
Web 服务的启动
首先我们来看下 AnnotationConfigServletWebServerApplicationContext
类关系图,由于图比较大就不放出来了。
AnnotationConfigServletWebServerApplicationContext --> ServletWebServerApplicationContext --> GenericWebApplicationContext --> GenericApplicationContext --> AbstractApplicationContext
所以在执行 AbstractApplicationContext.refresh 中调用 onRefresh
时,会调用 ServletWebServerApplicationContext.onRefresh
再调用 createWebServer
// ServletWebServerApplicationContext.createWebServer
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
//如果配有配置WebServer,则从BeanFactory中获取一个ServletWebServerFactory的实例
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
// 调用selfInitialize,配置servletContext,注册servletContext到spring context中,并调用ServletContextInitializer的onStartup钩子方法
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
// 初始化servletContext配置
initPropertySources();
}
总结
最后,我们通过一图简单解答前言中提到的问题。
当 spring boot 启动完成后,正常情况下一次 http 请求大致会经历这样一个过程。用户输入访问地址 --> 浏览器客户端发起请求 --> web 服务(tomcat、jetty)转发请求 --> 请求处理(servlet规范、reactive规范、其他自定义的) --> 响应结果
注:第一次发博客,难免有所不足,甚至出现错误的地方,欢迎大家指正,谢谢。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。