作者:mujun_
链接:https://www.jianshu.com/p/017a7f40efff
来源:简书
Spring Boot 2.0.2 版本的嵌入式Servlet容器自动配置是通过WebServerFactoryCustomizer定制器来定制的,而在Spring Boot 1.x 版本中我们是通过EmbeddedServletContainerCustomizer嵌入式的Servlet容器定制器来定制的。网上已经有很多对1.x版本自动配置及启动原理的分析,对2.x版本后的原理分析是很难找到的,目前Spring官网推荐使用Spring Boot 2.0.2 版本,下面将结合这个版本的源码,来对内嵌的Servlet容器的自动配置及启动作一个分析。
Spring Boot 通过运行主程序类的main()方法来启动Spring Boot 应用,应用启动后调用ServletWebServerApplicationContext这个类中的createWebServer()方法:
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = this.getWebServerFactory();
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}
this.initPropertySources();
}
该方法最终能够获取到一个与当前应用所导入的Servlet类型相匹配的web服务工厂定制器,例如你的pom文件导入的Servlet依赖为Tomcat:
那么最终会生成Tomcat web服务工厂定制器,定制Servlet容器配置,并通过上述代码中的getWebServer()方法创建对应的Servlet容器,并启动容器。
ServletWebServerFactory factory = this.getWebServerFactory();
上述代码执行来到EmbeddedWebServerFactoryCustomizerAutoConfiguration(嵌入式web服务工厂定制器自动配置类),根据导入的依赖信息,该配置类会自动创建相应类型的容器工厂定制器(目前Spring Boot 2.x 版本支持tomcat、jetty、undertow、netty),以tomcat为例,这里会创建TomcatWebServerFactoryCustomizer组件:
//导入的Servlet依赖为Tomcat,则创建Tomcat web服务工厂定制器
@ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
public static class TomcatWebServerFactoryCustomizerConfiguration {
public TomcatWebServerFactoryCustomizerConfiguration() {
}
@Bean
public TomcatWebServerFactoryCustomizertomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment,serverProperties);
}
}
需要注意的是,该自动配置类有一个@EnableConfigurationProperties注解:
@EnableConfigurationProperties({ServerProperties.class})
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
该注解用于启动指定类ServerProperties(Servlet容器相关的配置类)中的ConfigurationProperties功能,将配置文件中对应的属性值与配置类中的属性值进行映射,并将该配置类添加到IOC容器中:
@ConfigurationProperties(
prefix = "server",
ignoreUnknownFields = true
)
public class ServerProperties {
至此,TomcatWebServerFactoryCustomizer组件创建完成,对应的服务配置类也已添加到IOC容器。
下面的处理涉及到一个非常重要的类–>WebServerFactoryCustomizerBeanPostProcessor(web服务工厂定制器组件的后置处理器),该类负责在bean组件初始化之前执行初始化工作。
该类先从IOC容器中获取所有类型为WebServerFactoryCustomizer(web服务工厂定制器)的组件:
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
通过源码的调试,从IOC容器中获取到5个定制器,其中包含有前面创建的TomcatWebServerFactoryCustomizer定制器:
获取到所有的定制器后,后置处理器调用定制器的customize()方法来给嵌入式的Servlet容器进行配置(默认或者自定义的配置):
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
customizer.customize(webServerFactory);
});
}
TomcatWebServerFactoryCustomizer定制器调用customize()定制方法,获取到Servlet容器相关配置类ServerProperties,进行自动配置:
public void customize(ConfigurableTomcatWebServerFactory factory) {
ServerProperties properties = this.serverProperties;
Tomcat tomcatProperties = properties.getTomcat();
PropertyMapper propertyMapper = PropertyMapper.get();
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds).as(Long::intValue).to(factory::setBackgroundProcessorDelay);
this.customizeRemoteIpValve(factory);
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive).to((maxThreads) -> {
this.customizeMaxThreads(factory, tomcatProperties.getMaxThreads());
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive).to((minSpareThreads) -> {
this.customizeMinThreads(factory, minSpareThreads);
});
propertyMapper.from(() -> {
return this.determineMaxHttpHeaderSize();
}).when(this::isPositive).to((maxHttpHeaderSize) -> {
this.customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMaxHttpPostSize).when((maxHttpPostSize) -> {
return maxHttpPostSize != 0;
}).to((maxHttpPostSize) -> {
this.customizeMaxHttpPostSize(factory, maxHttpPostSize);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getAccesslog).when(Accesslog::isEnabled).to((enabled) -> {
this.customizeAccessLog(factory);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
properties.getClass();
propertyMapper.from(properties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> {
this.customizeConnectionTimeout(factory, connectionTimeout);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive).to((maxConnections) -> {
this.customizeMaxConnections(factory, maxConnections);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive).to((acceptCount) -> {
this.customizeAcceptCount(factory, acceptCount);
});
this.customizeStaticResources(factory);
this.customizeErrorReportValve(properties.getError(), factory);
}
至此,嵌入式Servlet容器的自动配置完成。
从源码分析可以得出配置嵌入式Servlet容器的两种解决方案:
1、在全局配置文件中,通过server.xxx来修改和server有关的配置:
server.port=8081
server.tomcat.xxx...
2、实现WebServerFactoryCustomizer接口,重写它的customize()方法,对容器进行定制配置:
@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory> {
void customize(T factory);
}
分析ServletWebServerApplicationContext类的createWebServer()方法:
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = this.getWebServerFactory();
//执行下面的代码,创建嵌入式的Servlet容器
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}
this.initPropertySources();
}
应用启动后,根据导入的依赖信息,创建了相应的Servlet容器工厂,以tomcat为例,创建嵌入式的Tomcat容器工厂TomcatServletWebServerFactory,调用getWebServer()方法创建Tomcat容器:
public WebServer getWebServer(ServletContextInitializer... initializers) {
//创建嵌入式的Tomcat容器
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}
...
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
//调用TomcatWebServer类的有参构造器
//如果配置的端口号>=0,创建Tomcat容器
return new TomcatWebServer(tomcat, this.getPort() >= 0);
}
...
分析TomcatWebServer类的有参构造器:
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
this.monitor = new Object();
this.serviceConnectors = new HashMap();
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.initialize();
}
执行initialize(),调用start()方法完成了tomcat容器的启动:
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
Object var1 = this.monitor;
synchronized(this.monitor) {
try {
this.addInstanceIdToEngineName();
Context context = this.findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && "start".equals(event.getType())) {
this.removeServiceConnectors();
}
});
//启动tomcat容器
this.tomcat.start();
this.rethrowDeferredStartupExceptions();
总结:
1、Spring Boot 根据导入的依赖信息,自动创建对应的web服务工厂定制器;
2、web服务工厂定制器组件的后置处理器获取所有类型为web服务工厂定制器的组件(包含实现WebServerFactoryCustomizer接口,自定义的定制器组件),依次调用customize()定制接口,定制Servlet容器配置;
3、嵌入式的Servlet容器工厂创建tomcat容器,初始化并启动容器。