title: 【SpringBoot】Servlet容器
date: 2017-08-31 21:25:12
tags:
- Java
- Spring
categories: Spring
记得自己看 Spring Boot 源码的初衷是对部署时不需要额外的 Servlet 容器的好奇,好像看着看着关注到了其他细节。挖了几个坑就跳过了,今天把之前关于 Spring Boot 与 Servlet 容器的坑填一下。
先回(rang)顾(wo)一(xiang)下(xiang)之前与 Web 环境相关的内容:
- SpringBoot包文件执行分析,这里说到开启了新线程调用应用入口,该线程设置了上下文类加载器 LaunchedURLClassLoader
- 【SpringBoot】SpringApplication实例创建,这里说到根据 CLASSPTH 的包含的类推断当前是否是 web 环境
- 【SpringBoot】容器启动,这里说到根据是否 web 环境创建做相关准备,创建处理 web 环境对应的 Spring容器
- 【Spring容器刷新】,这里说到 Spring 容器生命周期的相关方法
Servlet 容器启动
【SpringBoot】容器启动 中提到的创建处理 web 环境对应的 Spring 容器实际创建的就是 AnnotationConfigEmbeddedWebApplicationContext,这个类是 ApplicationContext 的子类。其分别在 onRefresh 和 finishRefresh 方法中创建和启动 Servlet 容器:
@Override
protected void onRefresh() {
super.onRefresh();
try {
createEmbeddedServletContainer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start embedded container", ex);
}
}
@Override
protected void finishRefresh() {
super.finishRefresh();
EmbeddedServletContainer localContainer = startEmbeddedServletContainer();
if (localContainer != null) {
publishEvent(
new EmbeddedServletContainerInitializedEvent(this, localContainer));
}
}
在 Spring 容器生命周期里与 Servlet 容器相关的逻辑封装在以上两个方法,分别用以创建和启动 Servlet 容器。
Servlet 容器创建细节
上面说到 AnnotationConfigEmbeddedWebApplicationContext 中创建 Servlet 容器,具体的细节如下:
// AnnotationConfigEmbeddedWebApplicationContext
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
if (localContainer == null && localServletContext == null) {
// 获取 Servlet 容器工厂
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
// 获取 Servlet 容器初始化器
// 使用 Servlet 容器初始化器创建 Servlet 容器
this.embeddedServletContainer =
containerFactory.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
try {
getSelfInitializer().onStartup(localServletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
细节可以分为:
- 获取 Servlet 容器工厂
- 获取 Servlet 容器初始化器
- 创建 Servlet 容器
获取 Servlet 容器工厂
// AnnotationConfigEmbeddedWebApplicationContext
protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory()
.getBeanNamesForType(EmbeddedServletContainerFactory.class);
// 省略异常
return getBeanFactory().getBean(beanNames[0],
EmbeddedServletContainerFactory.class);
}
直接从 Spring 容器里获取 Servlet 容器工厂。一时之间卡住了不知道 EmbeddedServletContainerFactory 这个类时怎么注册到 Spring 容器中的。在网上查了一下发现是 META-INF/spring.factories 里指定了 EmbeddedServletContainerAutoConfiguration 这个配置类。而 EmbeddedServletContainerFactory 是 EmbeddedServletContainerAutoConfiguration 这个自动化配置类中被注册到 Spring 容器中的,关于配置类的注册我又要挖坑了。EmbeddedServletContainerAutoConfiguration:
@AutoConfigureOrder(-2147483648)
@Configuration
@ConditionalOnWebApplication // 在Web环境下起作用
@Import({EmbeddedServletContainerAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class EmbeddedServletContainerAutoConfiguration {
public EmbeddedServletContainerAutoConfiguration() {
}
// 在 import 中导入该类
// 主要作用是注册 EmbeddedServletContainerCustomizerBeanPostProcessor,
// ErrorPageRegistrarBeanPostProcessor
public static class BeanPostProcessorsRegistrar
implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
// 省略
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if(this.beanFactory != null) {
// 注册 EmbeddedServletContainerCustomizerBeanPostProcessor
this.registerSyntheticBeanIfMissing(
registry, "embeddedServletContainerCustomizerBeanPostProcessor",
EmbeddedServletContainerCustomizerBeanPostProcessor.class);
// 注册 ErrorPageRegistrarBeanPostProcessor
this.registerSyntheticBeanIfMissing(
registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
}
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
String name, Class> beanClass) {
if(ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass,
true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
// 省略 Jetty,Undertow 配置
@Configuration
@ConditionalOnClass({Servlet.class, Tomcat.class})
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedTomcat {
public EmbeddedTomcat() {
}
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
// 注册 EmbeddedServletContainerFactory 的实现类
return new TomcatEmbeddedServletContainerFactory();
}
}
}
当满足特定条件时会注册具体的 EmbeddedServletContainerFactory 实现类,例如 TomcatEmbeddedServletContainerFactory。
这里还看到 注册了 EmbeddedServletContainerCustomizerBeanPostProcessor 和 ErrorPageRegistrarBeanPostProcessor 。简单看了一下 EmbeddedServletContainerCustomizerBeanPostProcessor,这是一个基本的 BeanPostProcessor,具体作用是对 EmbeddedServletContainerCustomizer 的实例进行定制,具体的实现包括:ErrorPageCustomizer,TomcatWebSocketContainerCustomizer,ServerProperties 等。
// EmbeddedServletContainerCustomizerBeanPostProcessor
public class EmbeddedServletContainerCustomizerBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List customizers;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainer) {
// 处理ConfigurableEmbeddedServletContainer类型的bean
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
private void postProcessBeforeInitialization(
ConfigurableEmbeddedServletContainer bean) {
// 对ConfigurableEmbeddedServletContainer类型的bean定制化处理
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
private Collection getCustomizers() {
if (this.customizers == null) {
// 从容器中找出所有EmbeddedServletContainerCustomizer类型的bean
this.customizers = new ArrayList(
this.beanFactory.getBeansOfType(EmbeddedServletContainerCustomizer.class,
false, false).values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
}
Servlet 容器初始化器
再回顾一下 Servlet 容器的创建核心逻辑:
containerFactory.getEmbeddedServletContainer(getSelfInitializer());
先获取 Servlet 初始化器,然后创建 Servlet 容器
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
selfInitialize(servletContext);
}
};
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
// Servlet 容器准备
prepareEmbeddedWebApplicationContext(servletContext);
// 初始 scopes
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
beanFactory);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
getServletContext());
existingScopes.restore();
// 注册 web 相关 bean
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
getServletContext());
// 初始化 servlet, filter, Listener 并注册
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
// 获取 ServletContext 初始化 bean
protected Collection getServletContextInitializerBeans() {
new ServletContextInitializerBeans(getBeanFactory());
}
public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
this.initializers = new LinkedMultiValueMap, ServletContextInitializer>();
// 添加 ServletContextInitializer 类型的 bean
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List sortedInitializers =
new ArrayList();
for (Map.Entry, List> entry : this.initializers.entrySet()) {
AnnotationAwareOrderComparator.sort(entry.getValue());
sortedInitializers.addAll(entry.getValue());
}
this.sortedList = Collections.unmodifiableList(sortedInitializers);
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Entry initializerBean : getOrderedBeansOfType(
beanFactory, ServletContextInitializer.class)) {
addServletContextInitializerBean(initializerBean.getKey(),
initializerBean.getValue(), beanFactory);
}
}
private void addServletContextInitializerBean(String beanName,
ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
// servlet
Servlet source = ((ServletRegistrationBean) initializer).getServlet();
addServletContextInitializerBean(Servlet.class, beanName, initializer,
beanFactory, source);
}
else if (initializer instanceof FilterRegistrationBean) {
// filter
Filter source = ((FilterRegistrationBean) initializer).getFilter();
addServletContextInitializerBean(Filter.class, beanName, initializer,
beanFactory, source);
}
else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
// filter
String source = ((DelegatingFilterProxyRegistrationBean) initializer)
.getTargetBeanName();
addServletContextInitializerBean(Filter.class, beanName, initializer,
beanFactory, source);
}
else if (initializer instanceof ServletListenerRegistrationBean) {
// listener
EventListener source = ((ServletListenerRegistrationBean>) initializer)
.getListener();
addServletContextInitializerBean(EventListener.class, beanName, initializer,
beanFactory, source);
}
else {
addServletContextInitializerBean(ServletContextInitializer.class, beanName,
initializer, beanFactory, initializer);
}
}
创建 Servlet 容器
以 TomcatEmbeddedServletContainerFactory 的创建容器方法为例:
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatEmbeddedServletContainer(tomcat);
}
上面的代码完成了 Servlet 容器启动前所有的创建,配置动作。
Servlet 容器启动
private EmbeddedServletContainer startEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
if (localContainer != null) {
localContainer.start();
}
return localContainer;
}
启动的逻辑并不复杂,直接调用 Servlet 容器的 start 方法。
总结
Spring Boot 使用的内嵌 Servlet 容器启动过程:
- 从 spring.factories 中读取并注册了 EmbeddedServletContainerAutoConfiguration 类
- EmbeddedServletContainerAutoConfiguration 注册了 Servlet 容器工厂类
- 在 Spring 容器生命周期的 onRefresh 方法中开始创建 Servlet 容器
- 获取 Servlet 容器工厂
- 获取 Servlet 相关初始化 bean
- 配置并创建 Servlet 容器
- 在 Spring 容器生命周期的 finishRefresh 方法中调用 Servlet 容器的 start 方法启动容器