SpringBoot Tomcat 定制化实现原理
SpringBoot Tomcat 配置定制化,可以通过两种方式:
- Springboot 默认提供属性配置,比如server.tomcat.XXX
- 通过编程方式实现Tomcat 属性配置,通过编程方式有两种方法
1. 实现TomcatConnectorCustomizer/TomcatContextCustomizer/TomcatProtocolHandlerCustomizer
2. 实现WebServerFactoryCustomizer,并实现Ordered接口
SpringBoot 默认提供Tomcat属性配置
springbootTomcat 常用配置如下:
属性 | 属性默认值 | 属性说明 |
---|---|---|
basedir | 默认为临时目录 | tomcat 运行目录–建议覆盖默认配置 |
threads.max | 默认为200 | tomcat 最大线程数 |
threads.minSpare | 默认为10 | tomcat 最小线程数 |
uriEncoding | 默认为UTF8 | URI编码格式 |
maxConnections | 默认为8192 | tomcat 最大连接数 |
acceptCount | 默认为100 | 接请求的最大队列长度 |
connectionTimeout | 默认为6秒 | 连接超时时间 |
编程方式实现Tomcat属性配置
通过TomcatXXXCustomizer来实现
@Configuration(proxyBeanMethods = false)
public class MyTomcatConnector implements TomcatConnectorCustomizer {
@Override
public void customize(Connector connector) {
connector.setPort(9090);
System.out.println("connector设置为9090" );
}
}
通过WebServerFactoryCustomizer来实现
/**
* 添加AJP支持
*/
@Configuration
@Order(3)
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, ApplicationContextAware {
private ApplicationContext context;
@Override
public void customize(TomcatServletWebServerFactory factory) {
if (context instanceof WebServerApplicationContext) {
TomcatWebServer webServer = ((TomcatWebServer) ((WebServerApplicationContext) context).getWebServer());
//webServer.getTomcat().setPort(9091);
}
factory.setPort(9091);
System.out.println("connector设置为9091");
Connector ajp = new Connector("AJP/1.3");
ajp.setPort(8085);
ajp.setSecure(false);
ajp.setScheme("http");
AjpNioProtocol ajpNioProtocol = (AjpNioProtocol) ajp.getProtocolHandler();
ajpNioProtocol.setSecretRequired(false);
factory.addAdditionalTomcatConnectors(ajp);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
探讨下两种配置方式的优先级问题
Springboot 默认提供属性配置< WebServerFactoryCustomizer(order>0) < TomcatXXXCustomizer
- 默认WebServerFactoryCustomizer优先级低于TomcatXXXCustomizer,就是TomcatXXXCustomizer会覆盖WebServerFactoryCustomizer的属性配置。
- WebServerFactoryCustomizer 基于WebServerFactoryCustomizerBeanPostProcessor来实现属性赋值
- SpringBoot提供tomcat属性配置基于TomcatWebServerFactoryCustomizer来实现,默认优先级为0
源码解析
TomcatXXXCustomizer
- 收集TomcatXXXCustomizer的bean-ServletWebServerFactoryConfiguration
@Bean
//收集TomcatXXXCustomizer的bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
- 调用TomcatXXXCustomizer来自于org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
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
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List<WebServerFactoryCustomizer<?>> customizers;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
"WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
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;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
//获取spring 容器里面所有的WebServerFactoryCustomizer bean,如果没有实例化进行实例化
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
}