在 Java - Servlet 3.0 这篇文章中,我们知道在 Servlet 3.0 之后,可以使用 注解来配置 servlet 以及 运行时插件可插拔 能力。
SpringBoot(使用版本:2.3.12.RELEASE) 为我们提供了开箱即用的 web 环境,当然也提供了与 Servlet 3.0 的集成,核心在 org.springframework.boot.web.servlet 包中:
ServletContextInitializer
在 Java - Servlet 3.0 这篇文章中,我们知道 Servlet 3.0 提供了 ServletContainerInitializer 接口来提供插件化能力,SpingBoot 提供了 ServletContextInitializer 接口,以编程的方式来配置 Servlet 3.0+ 上下文,但是生命周期是由 Spring 来管理的,而不是 Servlet 容器。
集成 Servlet 容器在 SpringBoot - 内嵌 tomcat 等 servlet 容器的原理 这篇文章中,我们知道 SpringBoot 默认使用 Tomcat 作为 servlet 容器,在 SpringBoot 的启动流程中,ServletWebServerApplicationContext 类中的 createWebServer() 方法用于创建 WebServer。
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
// [1]getSelfInitializer():返回用于完成 WebApplicationContext 设置的 ServletContextInitializer
// [2]getWebServer():会完成 ServletContextInitializer 转换为 ServletContainerInitializer
this.webServer = factory.getWebServer(getSelfInitializer());
...
}
...
}
[1] getSelfInitializer()
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
// 准备 WebApplicationContext
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// getServletContextInitializerBeans:获取所有 ServletContextInitializer 类型的 Bean
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
查看 getServletContextInitializerBeans() 方法,返回了一个集合:ServletContextInitializerBeans
protected CollectionServletContextInitializerBeansgetServletContextInitializerBeans() { return new ServletContextInitializerBeans(getBeanFactory()); }
ServletContextInitializerBeans 类实现了 AbstractCollection,并重写了 iterator() 和 size(),遍历的自然也就是 iterator() 中使用的成员变量 sortedList
public class ServletContextInitializerBeans extends AbstractCollection{ ... private final MultiValueMap , ServletContextInitializer> initializers; private final List > initializerTypes; private List sortedList; ... @Override public Iterator iterator() { return this.sortedList.iterator(); } @Override public int size() { return this.sortedList.size(); } ... }
查看 ServletContextInitializerBeans 集合类的构造器:
@SafeVarargs
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
// 这里我们没有在构造器中传 initializerTypes,所以默认为 ServletContextInitializer
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// 核心方法
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
查看 addServletContextInitializerBeans(beanFactory) 方法:
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
// 这里的 this.initializerTypes 就是 ServletContextInitializer
for (Class extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry initializerBean :
// 获取所有 initializerType 类型的 Bean 并排序
getOrderedBeansOfType(beanFactory, initializerType)) {
// 添加 ServletContextInitializerBean 到成员变量:initializers 中,是一个 Spring 实现 Map 接口的 MultiValueMap 类型
addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
}
}
}
打断点获取到 getOrderedBeansOfType() 方法返回的值,默认是 DispatcherServletRegistrationBean:
接着遍历上面 getOrderedBeansOfType() 方法中获取到的 Bean,判断是属于 ServletRegistrationBean、FilterRegistrationBean、DelegatingFilterProxyRegistrationBean、ServletListenerRegistrationBean,并添加到成员变量:initializers 中:
private void addServletContextInitializerBean(Class> type, String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory, Object source) {
this.initializers.add(type, initializer);
...
}
接着在构造器中按指定规则排序并赋值给成员变量 sortedList:
@SafeVarargs
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class extends ServletContextInitializer>... initializerTypes) {
...
// 赋值 sortedList
List sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
接着会遍历 sortedList,并调用其中的 Bean 的 onStartUp 方法:
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
// 调用 onStartUp 方法
beans.onStartup(servletContext);
}
}
[2] getWebServer()
ServletWebServerFactory
SpringBoot 对所支持的 Servlet 容器 做了抽象处理,提取出了工厂类:ServletWebServerFactory
@FunctionalInterface
public interface ServletWebServerFactory {
WebServer getWebServer(ServletContextInitializer... initializers);
}
常用的 Servlet 容器 实现:
| Servlet 容器类型 | WebServer 模型接口 | WebServer 工厂实现类 |
|---|---|---|
| Tomcat | TomcatWebServer | TomcatServletWebServerFactory |
| Jetty | JettyWebServer | JettyServletWebServerFactory |
| Undertow | UndertowWebServer | UndertowServletWebServerFactory |
类路径下有且只能有一个 ServletWebServerFactory 的实现类:
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
private void createWebServer() {
...
this.webServer = factory.getWebServer(getSelfInitializer());
...
}
从代码中可以看到,getWebServer 是把 getSelfInitializer() 返回的 ServletWebServerApplicationContext 作为参数:
查看 TomcatServletWebServerFactory 中的 getWebServer 方法:
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
...
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
...
context.addLifecycleListener(new StaticResourceConfigurer(context));
// 组合 SpringBoot 需要初始化的所有 ServletContextInitializer
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
host.addChild(context);
// 配置上下文环境
configureContext(context, initializersToUse);
postProcessContext(context);
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
context.addServletContainerInitializer(starter, NO_CLASSES);
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
...
}
TomcatStarter 是 SpringBoot 对于 嵌入式 Tomcat 的定义,实现了 ServletContainerInitializer,查看源码:
class TomcatStarter implements ServletContainerInitializer {
private static final Log logger = LogFactory.getLog(TomcatStarter.class);
private final ServletContextInitializer[] initializers;
private volatile Exception startUpException;
// 会把 ServletContextInitializer 作为构造器参数传进来
TomcatStarter(ServletContextInitializer[] initializers) {
this.initializers = initializers;
}
@Override
public void onStartup(Set> classes, ServletContext servletContext) throws ServletException {
try {
// Tomcat 启动的时候会调用 onStartUp 方法
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
this.startUpException = ex;
// Prevent Tomcat from logging and re-throwing when we know we can
// deal with it in the main thread, but log for information here.
if (logger.isErrorEnabled()) {
logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
+ ex.getMessage());
}
}
}
Exception getStartUpException() {
return this.startUpException;
}
}
总结
通过以上分析,源码追踪,我们知道了 SpringBoot 是如何结合 Tomcat 等 Servlet 容器 实现 Servlet、Filter、Listerner 等组件与 Spring IoC 容器 结合并实现加载和初始化的。
从 ServletContextInitializer 的关系类图中可以看到,SpringBoot 定义了 ServletRegistrationBean、ServletListenerRegistrationBean、FilterRegistrationBean、DelegatingFilterProxyRegistrationBean 等基于 ServletContextInitializer 接口的实现类来实现组件的加载,在 SpringBoot 学习专栏 中会具体讲解每一种 RegistrationBean 的用法,欢迎订阅关注。



