SpringBoot最重要的功能功能之一就是自动装配,他会去读取SpringFrameWork中autoConfiguration包下的spring.factories文件,然后将其中配置的AutoConfiguration通过规则匹配之后自动装配到Spring容器中(ServletWebServerFactoryAutoConfiguration),这个类作用就是,当我们点击SpringApplication.run方法的时候,底层会帮我们启动对应的服务(Tomcat,underTow,Jetty)中的一个
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
}
@EnableConfigurationProperties(ServerProperties.class):这个注解会去解析配置文件中server为前缀的字串,然后统一映射成ServerProperties类为我们使用,比如我们常见的server.port = 8080,这个port就是ServerProperties中的属性名
@Import注解引入了四个类,我们先来看后面三个(Tomcat,Jetty,UnderTow),和Tomcat相关的内部类,就是定义了一个Bean,并且返回一个ServletWebServerFactory类型的对象,在中间的代码中我们可以自定义ConnectorCustomizers这些类,来设置启动的属性,比如,jetty和UnderTow也是一样的,这边拿Tomcat看一下;
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
//MissingBean中说明了不允许存在其他的ServletWebServerFactory类型,如果存在,那么这边就不会生效,所以保证了服务的唯一
static class EmbeddedTomcat {
//@Bean方法中的参数如果不加ObjectProvider,那么便会强依赖与他的入参,如果没有便会报错,
//如果加了ObjectProvider,那么在Spring容器中就算没有这些类型的参数,也会默认赋值一个空对象
@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;
}
}
//可以通过自定义的方式来为设置Tomcat中的参数
@Bean
TomcatConnectorCustomizer getTomcatConnectorCustomizer(){
return new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
connector.setPort(8081);
}
};
}
ConditionalOnMissingBean条件注解中说明了不允许存在其他的ServletWebServerFactory类型,如果存在,那么这边就不会生效,所以保证了服务的唯一。这个ServletWebServerFactory是个工厂,SpringBoot会通过调用其中的getWebServer方法来启动对应的服务(下面代码略有删减,看核心代码即可),我们上述可以通过server.port来为tomcat赋值,也可以通过自定义ConnectorCustomizer来修改参数,在customizeConnector方法中,可以看到有先后顺序,server.port在前,自定义的会覆盖server.port的操作(getPort()为什么会返回server.port最后会有解释)
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
Connector connector = new Connector(this.protocol);
//这里面便会调用上面那张图片中的customize方法来为Tomcat赋值
customizeConnector(connector);
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
protected void customizeConnector(Connector connector) {
//server.port,配置文件中定义的端口
int port = Math.max(getPort(), 0);
//最后一张图有解析
connector.setPort(port);
//我们自定义的连接对象
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
customizer.customize(connector);
}
}
最后再来看一下BeanPostProcessorsRegistrar,@import导入的第一个注解,他实现了ImportBeanDefinitionRegistrar接口,在Spring的启动过程中便会调用到registerBeanDefinition方法,他为我们注册了一个WebServerFactoryCustomizerBeanPostProcessor的BeanPostProcessor进去
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
}
在其postProcessBeforeInitialization方法中,通过阅读代码得知,就是拿到Spring中的WebServerFactoryCustomizer,然后去执行里面的customize方法
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
//最终会执行WebServerFactory中的customize方法
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
WebServerFactoryCustomizer在第一张图中我们能看到,Spring自定义了一个这样类型的Bean进去,所以我们直接解析ServletWebServerFactoryCustomizer.customize方法,里面的方法意思就是从ServerProperties中拿到对应的属性,然后塞到通过set方法注入到factory中,对应第三张图的getPort()方法就形成了闭环,吧ServerProperties文件中的属性都塞到了Tomcat/UnderTow/Jetty中
public void customize(ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.serverProperties::getPort).to(factory::setPort);
map.from(this.serverProperties::getAddress).to(factory::setAddress);
map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);
map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);
map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);
map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
map.from(this.serverProperties::getSsl).to(factory::setSsl);
map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
map.from(this.serverProperties::getCompression).to(factory::setCompression);
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
}
总结:
通过得到WebServerFactory,然后通过工厂模式得到对应的服务并且启动,在启动的过程中,可以手动实现一些Customizer来设置参数,也可以通过配置文件的方式来设置参数,手动创建的会覆盖配置文件
//得到对应的工厂
//TomcatServletWebServerFactory
//JettyServletWebServerFactory
//UndertowServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
//根据工厂启动容器,tomcat/jetty/underTow
this.webServer = factory.getWebServer(getSelfInitializer());
只允许启动一个服务
//只允许存在一个,也就是只能启动一个服务
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}