tomcat 外部配置
前面我们分析了 spring boot 与 tomcat 的集成,我们再来看看 spring boot 是如何为 serlvet 容器设置参数的。首先,来看一下常用的配置,内嵌容器的配置以 server
开头,下面的示例采用 yml 格式,properties 文件只是格式略有不同而已
application.yml
server:
port: 8080
context-path: /driver
tomcat:
max-threads: 500
max-connections: 20000 # 最大允许的连接数,nio默认10000
spring boot 关于内嵌 servlet 容器主要依赖 ServerProperties
完成,下面的代码列举了常用的配置,注意:代码里面的 Tomcat
、Jetty
是内部类,用于进行参数配置的,并不是对应的 servlet 容器。比如我们要设置 tomcat 的参数,使用 server.tomcat
前缀即可,jetty 对应就是 server.jetty
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties
implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {
private Integer port;
private String contextPath;
private String servletPath = "/";
private final Tomcat tomcat = new Tomcat();
private final Jetty jetty = new Jetty();
private final Undertow undertow = new Undertow();
public static class Tomcat {
private final Accesslog accesslog = new Accesslog();
private int maxThreads = 0; // Number of threads in protocol handler
private int maxConnections = 0;
private int acceptCount = 0;
}
public static class Jetty { //...... }
public static class Undertow { //...... }
//......
spring boot 是如何为 servlet 容器设置这些参数的呢?我们注意到 ServerProperties
实现了 EmbeddedServletContainerCustomizer
接口,通过这个接口便可以进行参数设置,下面列出了部分代码。首先,对通用参数进行设置,比如 端口
、上下文路径
、session 超时时间
等等,然后判断 ConfigurableEmbeddedServletContainer
的具体实现类,分别对具体的 servlet 容器进行配置,因为不同 servlet 容器的参数是不一样的,所以需要特殊处理。如果我们需要对 tomcat 容器进行额外的设置,可以实现 EmbeddedServletContainerCustomizer
接口,然后把这个 bean 注册到 spring 容器中即可
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (getPort() != null) {
container.setPort(getPort());
}
// other code......
// 根据具体的实现类,分别对具体的 servlet 容器进行配置
if (container instanceof TomcatEmbeddedServletContainerFactory) {
// 即 private final Tomcat tomcat = new Tomcat();
getTomcat().customizeTomcat(this,
(TomcatEmbeddedServletContainerFactory) container);
}
if (container instanceof JettyEmbeddedServletContainerFactory) {
getJetty().customizeJetty(this,
(JettyEmbeddedServletContainerFactory) container);
}
if (container instanceof UndertowEmbeddedServletContainerFactory) {
getUndertow().customizeUndertow(this,
(UndertowEmbeddedServletContainerFactory) container);
}
container.addInitializers(new SessionConfiguringInitializer(this.session));
container.addInitializers(new InitParameterConfiguringServletContextInitializer(
getContextParameters()));
EmbeddedServletContainerCustomizer
又是何时被调用的呢?它是通过 BeanPostProcessor
进行扩展实现的,从 spring 容器中获取 ConfigurableEmbeddedServletContainer
对象时,便会执行该 BeanPostProcessor
,这里再次感受到 BeanPostProcessor
的强大之处。
public class EmbeddedServletContainerCustomizerBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainer) {
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
private void postProcessBeforeInitialization(ConfigurableEmbeddedServletContainer bean) {
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
总结
在此,spring boot 集成 tomcat 的代码分析便告一段落,最后,我们用一张图总结下整个逻辑(查看原图)
- spring boot 利用 spring 的 SPI 的机制加载 EmbeddedServletContainerAutoConfiguration 该配置类,将 TomcatEmbeddedServletContainerFactory 加载到 spring 容器中
- 对 tomcat 容器进行配置的动作,由 BeanFactoryPostProcessor 完成,spring boot 内置了多种 EmbeddedServletContainerCustomizer,由 ServerConfig 完成对 servlet 容器的配置
- 利用工厂模式创建 TomcatEmbeddedServletContainer,并且调用
org.apache.catalina.startup.Tomcat#start()
启动 tomcat 容器