导言

Spring Boot,旨在简化传统Spring应用的开发、配置和部署流程,通过约定优于配置的原则,使得开发者可以快速启动和运行基于Spring的应用。应用层使用方便的同时,底层对Spring MVC进行了深度集成,自动处理常见的配置项,也通过简化依赖管理、自动配置功能对各种第三方技术进行了整合。

Spring Boot主要有两个核心

  1. 自动装配
    • @EnableAutoConfiguration
    • META-INF/spring.factories、SPI机制
    • 条件注解
  2. 启动原理
    • spring-boot-starter(分包上,将常用的依赖分组进行了整合,将其合并到一个依赖中)
    • SpringApplication.run

目前先只讲 SpringApplication.run中对Spring Framework、Spring MVC的集成,以整合前面所学的spring框架相关知识,了解主要的Spring Boot启动流程。

对比

1. 传统Spring框架整合Spring MVC

需要显式配置大量的组件

传统Spring框架集成Spring MVC的简单原理

idea集成Tomcat,自动启动与配置Tomcat,不用开发人员关心细节

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//1. 创建Spring容器
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 将容器存储到ServletContext域中
sce.getServletContext().setAttribute("applicationContext",app);
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}

ServletContextListener监听器的 contextInitialized()方法在 ServletContext创建时调用,即在Web应用启动时被触发。

contextInitialized()方法中将实例化应用上下文 ApplicationContext,并将应用上下文存储到 ServletContext域中,使所有servlet能够访问到同一个应用上下文。

web.xml中配置 DispatcherServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

2. Spring Boot集成Spring MVC

条件注解自动根据类路径配置组件(例如,如果检测到 spring-webmvc在类路径上,它将自动配置 DispatcherServlet

嵌入式Web服务器简化部署、快速启动

使用构建工具自动集成项目所有依赖版本

Web上下文

UML类图

image-20240525134247269

GenericApplicationContext

通用应用上下文实现,不依赖于Servlet API,Web和非Web环境都能使用,一个轻量级的容器实现。

GenericWebApplicationContext

实现了 WebApplicationContext接口,支持Servlet容器的环境,集成了 ServletContext,适用于Spring MVC应用。

ServletWebServerApplicationContext

专门为嵌入式Servlet容器场景设计,加入了对嵌入式Servlet Web服务器的支持,能够根据配置自动启动与管理Web服务器,与Spring Boot的自动配置紧密集成。

集成过程

1. refresh

首先要了解 refresh()方法中各步骤的执行顺序

注意事项:

  1. ServletWebServerApplicationContext中,先创建应用上下文,然后 refresh()方法刷新,在 refresh()方法中的 onRefresh()方法中再创建Tomcat,配置 ServletContext
  2. 无论是普通还是自动配置类,都会在 invokeBeanFactoryPostProcessors方法中被识别处理,将其中的bean设置为 BeanDefinitionbean定义对象。知道执行 finishBeanFactoryInitialization提前实例化方法或程序主动调用 getBean()时,bean定义对象才会接着执行后续的实例化与初始化操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
// 自动配置类通过条件注解被筛选识别注册
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 普通@Configuration配置类被识别处理,自动配置类被激活并具体处理
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化消息源
this.initMessageSource();
// 初始化事件广播器
this.initApplicationEventMulticaster();

// 初始化特定上下文子类中的其他特殊Bean
this.onRefresh(); // 这里是onRefresh方法被调用的位置

// 注册监听器
this.registerListeners();

// 提前实例化配置类中的所有bean
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}
}
}

2. 具体流程

集成的整个流程如下:

2.1 创建应用上下文

1
2
3
4
5
6
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

先在run方法中创建并刷新上下文

1
2
3
4
5
6
7
8
// SpringApplication
public ConfigurableApplicationContext run(String... args) {
// 创建ApplicationContext
ConfigurableApplicationContext context = createApplicationContext();
// 刷新ApplicationContext
refreshContext(context);
//...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// ServletWebServerApplicationContext
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
} catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}

2.2 创建嵌入式Tomcat

在刷新应用上下文的过程中,ServletWebServerApplicationContext类中重写的 onRefresh()方法创建并配置嵌入式Tomcat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ServletWebServerApplicationContext
@Override
protected void onRefresh() {
super.onRefresh();
try {
this.webServer = createWebServer();
this.webServer.start();
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
// 调用ServletWebServerFactory创建Web服务器
private WebServer createWebServer() {
ServletWebServerFactory factory = getBeanFactory().getBean(ServletWebServerFactory.class);
return factory.getWebServer(this::selfInitialize);
}

TomcatServletWebServerFactory自动配置类创建并配置嵌入式Tomcat(此处是 SmartMvcDispatcherServletRegistrationBean

2.3 将应用上下文引入设置到ServletContext中

1
2
3
4
5
6
7
8
9
// ServletWebServerApplicationContext
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
//...
}
private void prepareWebApplicationContext(ServletContext servletContext) {
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
setServletContext(servletContext);
}

2.4 创建DispatcherServlet并将其注册进ServletContext

DispatcherServletAutoConfiguration自动配置类负责(具体看下面)

Servlet注册机制

1. 传统注册方式

  1. web.xml
  2. @WebServlet
  3. ServletContainerInitializer接口

以前注册Servlet使用的是 web.xml@WebServlet的方式,还有一种编程方式注册Servlet的方法:实现 ServletContainerInitializer接口重写 onStartup()方法,例如:

1
2
3
4
5
6
7
8
// Servlet
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.getWriter().write("<h1>Hello, World from HelloServlet!</h1>");
}
}
1
2
3
4
5
6
7
8
9
// 注册Servlet
public class MyServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
ServletRegistration.Dynamic servlet = ctx.addServlet("HelloServlet", new HelloServlet());
servlet.addMapping("/hello");
servlet.setLoadOnStartup(1);
}
}

2. SpringFramework中

Spring Framework中,提供了 ServletContainerInitializer的实现 WebApplicationInitializer接口,容器自动发现 WebApplicationInitializer并调用其 onStartUp()方法。

3. SpringBoot中

Spring Boot进一步简化,不需要显式地实现 WebApplicationInitializer,提供了 ServletRegistrationBean来注册Servlet、Filter等,例如:

1
2
3
4
5
6
7
8
9
10
// 注册Servlet
@Configuration
public class ServletConfig {
@Bean
public ServletRegistrationBean<HelloServlet> helloServletRegistration() {
ServletRegistrationBean<HelloServlet> registrationBean = new ServletRegistrationBean<>(new HelloServlet(), "/hello");
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
}

4. SpringBoot注册Servlet底层原理

4.1 注册DispatcherServlet

Spring Boot中注册 DispatcherServletDispatcherServletAutoConfiguration自动配置类负责

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
@EnableConfigurationProperties(WebMvcProperties.class)
public class DispatcherServletAutoConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

// 创建DispatcherServlet
@Bean
@ConditionalOnMissingBean(value = DispatcherServlet.class)
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}

// 注册DispatcherServlet进ServletContext
@Bean
@ConditionalOnBean(value = DispatcherServlet.class)
public DispatcherServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
return registration;
}
}

先通过自动装配原理识别到该自动配置类,自动配置类中的bean在 refresh()方法的 finishBeanFactoryInitialization()方法中被实例化,该方法在 onRefresh()方法完全执行完毕之后调用,DispatcherServlet所需的环境(Tomcat)与初始化参数(应用上下文)都已被配置完成,能够成功被初始化。

4.2 注册普通Servlet

先是开发者自定义注册Servlet

1
2
3
4
5
6
7
8
9
10
// 注册Servlet
@Configuration
public class ServletConfig {
@Bean
public ServletRegistrationBean<HelloServlet> helloServletRegistration() {
ServletRegistrationBean<HelloServlet> registrationBean = new ServletRegistrationBean<>(new HelloServlet(), "/hello");
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
}

做完这一步后 ServletWebServerApplicationContext直接通过 getBeansOfType()方法实例化所有的Servlet对象,然后调用 onStartUp()方法Servlet注册进 ServletContext当中,完成Servlet对象的初始化,这部分代码如下。(onStartUp()方法好像还能注册其他web组件)

1
2
3
4
5
6
7
8
// ServletWebServerApplicationContext
private void selfInitialize(ServletContext servletContext) throws ServletException {
//...
Map<String, ServletContextInitializer> beanMaps = getBeanFactory().getBeansOfType(ServletContextInitializer.class);
for (ServletContextInitializer bean : beanMaps.values()) {
bean.onStartup(servletContext);
}
}