King's Studio

Spring Boot嵌入式Servlet容器自动配置原理

字数统计: 1.2k阅读时长: 5 min
2019/04/20 Share

以前的web应用开发我们采取的方式是项目完成后打包成war包,然后配置tomcat启动运行项目,而Spring Boot默认使用的是嵌入式的tomcat,那我们需要如何配置嵌入式的Servlet容器呢?

定制修改Servlet容器相关配置

修改和server有关的配置

我们可以到项目的配置文件中直接对server的属性进行修改。

配置文件

在ServerProperties中我们能够看到所有可以进行配置的属性。

容器配置

编写一个WebServerFactoryCustomizer:web服务器工厂定制器

在Spring Boot2.0及以上版本的学习过程中,我发现了多处与之前版本不同的地方,应该算是Spring Boot开发者的优化。之后我将专门写一篇来对2.0版本进行更新的总结。先说web服务器工厂定制器,在Spring Boot2.0中已经使用WebServerFactoryCustomizer取代了EmbeddedServletContainerCustomizer,当我们需要定制Servlet容器的时候,我们采取这样的方式。

1
2
3
4
5
6
7
8
9
10
11
12
//配置嵌入式的Servlet容器
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){

//定制嵌入式的Servlet容器相关的规则
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8083);
}
};
}

注册Servlet三大组件(Servlet、Filter、Listener)

以前的Spring项目注册三大组件是在项目下的webapp/WEB-INF/web.xml文件中进行配置,由于Spring Boot是以jar包的方式启动嵌入式的Servlet容器来启动web应用,它并没有web.xml文件,我们注册三大组件采用如下方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//注册三大组件Servlet
@Bean
public ServletRegistrationBean myServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
registrationBean.setLoadOnStartup(1);
return registrationBean;

}

//注册Filter
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new MyFilter());
filterRegistrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
return filterRegistrationBean;
}

//注册Listener
@Bean
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean<MyListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>(new MyListener());
return servletListenerRegistrationBean;
}

嵌入式Servlet容器自动配置原理

整个Spring Boot最核心的部分都在它的自动配置原理中,Spring Boot支持三种嵌入式Servlet容器:tomcat、jetty、undertow。在这里就不对切换做过多说明,我们主要研究自动配置原理。

ServletWebServerFactoryConfiguration:Servlet容器工厂的配置

在这边我们又发现了2.0版本中的更新,它将原来放在EmbeddedServletContainerAutoConfiguration中对TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory的配置放到了ServletWebServerFactoryConfiguration中,它会使用@ConditionalOnClass注解先判断是否已经有了Servlet以及其他的class,如果没有,@Configuration注解就会进行配置,我们进入到Spring Boot源码中进行查看。

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
43
44
45
46
47
@Configuration
class ServletWebServerFactoryConfiguration {

@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {

@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}

}

/**
* Nested configuration if Jetty is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {

@Bean
public JettyServletWebServerFactory JettyServletWebServerFactory() {
return new JettyServletWebServerFactory();
}

}

/**
* Nested configuration if Undertow is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {

@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
return new UndertowServletWebServerFactory();
}

}

}

以TomcatServletWebServerFactory为例

1
2
3
4
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
...
}

我们可以看到TomcatServletWebServerFactory实现了ConfigurableTomcatWebServerFactory接口,我们再进入到ConfigurableTomcatWebServerFactory中查看,能够发现它继承了ConfigurableWebServerFactory接口,并且JettyServletWebServerFactory、UndertowServletWebServerFactory都实现了ConfigurableWebServerFactory接口。

1
2
3
4
5
6
7
8
9
10
11
12
public interface ConfigurableTomcatWebServerFactory extends ConfigurableWebServerFactory {
...
}

public interface ConfigurableJettyWebServerFactory extends ConfigurableWebServerFactory {
...
}

public interface ConfigurableUndertowWebServerFactory
extends ConfigurableWebServerFactory {
...
}

我们能够看到getWebServer()方法中,创建了一个tomcat对象,并且最后将tomcat返回,启动了tomcat服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public WebServer getWebServer(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 getTomcatWebServer(tomcat);
}

嵌入式容器配置修改的生效

WebServerFactoryCustomizer:定制器帮助修改了Servlet容器的配置。修改的原理:容器中的WebServerFactoryCustomizerBeanPostProcessor后置处理器生效。

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
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
//如果当前初始化的是一个WebServerFactory类型的组件
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}

@SuppressWarnings("unchecked")
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe
.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}

private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}

配置原理总结

  • Spring Boot根据导入的情况,给容器中添加相应的如TomcatServletWebServerFactory。
  • 容器中某个组件要创建对象就会触发WebServerFactoryCustomizerBeanPostProcessor,只要是嵌入式的Servlet容器工厂,后置处理器就会工作。
  • WebServerFactoryCustomizerBeanPostProcessor后置处理器,从容器中获取所有的WebServerFactoryCustomizer,调用定制器的定制方法。

原文作者:金奇

原文链接:https://www.rossontheway.com/2019/04/20/SpringBoot嵌入式Servlet容器自动配置原理/

发表日期:April 20th 2019, 12:00:00 am

更新日期:April 20th 2019, 5:57:53 pm

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可,除特别声明外,转载请注明出处!

CATALOG
  1. 1. 定制修改Servlet容器相关配置
    1. 1.1. 修改和server有关的配置
    2. 1.2. 编写一个WebServerFactoryCustomizer:web服务器工厂定制器
    3. 1.3. 注册Servlet三大组件(Servlet、Filter、Listener)
  2. 2. 嵌入式Servlet容器自动配置原理
    1. 2.1. ServletWebServerFactoryConfiguration:Servlet容器工厂的配置
    2. 2.2. 以TomcatServletWebServerFactory为例
    3. 2.3. 嵌入式容器配置修改的生效
    4. 2.4. 配置原理总结