以前的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容器的时候,我们采取这样的方式。
//配置嵌入式的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文件,我们注册三大组件采用如下方式:
//注册三大组件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源码中进行查看。
@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为例
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
...
}
我们可以看到TomcatServletWebServerFactory实现了ConfigurableTomcatWebServerFactory接口,我们再进入到ConfigurableTomcatWebServerFactory中查看,能够发现它继承了ConfigurableWebServerFactory接口,并且JettyServletWebServerFactory、UndertowServletWebServerFactory都实现了ConfigurableWebServerFactory接口。
public interface ConfigurableTomcatWebServerFactory extends ConfigurableWebServerFactory {
...
}
public interface ConfigurableJettyWebServerFactory extends ConfigurableWebServerFactory {
...
}
public interface ConfigurableUndertowWebServerFactory
extends ConfigurableWebServerFactory {
...
}
我们能够看到getWebServer()方法中,创建了一个tomcat对象,并且最后将tomcat返回,启动了tomcat服务器。
@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后置处理器生效。
@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,调用定制器的定制方法。