🤖🤖-摘要:
本文介绍了SpringBoot Web开发中的嵌入式容器。讲解了Servlet容器及其三大组件,介绍了嵌入式服务器Tomcat、Jetty、Undertow的自动配置原理及Tomcat容器创建的具体方法。最后,文章也示范了如何在项目中切换服务器。

嵌入式容器

Servlet容器:管理、运行Servlet组件的环境,一般指服务器

Servlet三大组件:

  • Servlet, 处理请求
  • Filter, 过滤请求
  • Listener, 监听请求

自动配置原理浅析

先从自动配置类开始

@AutoConfiguration(after = SslAutoConfiguration.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 生效条件:实现ServletRequest接口的Servlet请求
@ConditionalOnClass(ServletRequest.class)
// 生效条件:是SERVLET类型的程序
@ConditionalOnWebApplication(type = Type.SERVLET)
// 绑定配置文件, server.*
@EnableConfigurationProperties(ServerProperties.class)
// 批量导入一些嵌入式服务器类
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
// ...
}

嵌入式的三大服务器:TomcatJettyUndertow

  • 导入TomcatJettyUndertow都有条件注解(系统中有对应的类才行,导包即可)
  • 默认Tomcat配置生效.web场景默认导入spring-boot-starter-tomcat
  • Tomcat配置生效后,会在容器中放入TomcatServletWebServerFactory组件,用于创建容器的工厂类
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {

@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().toList());
factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().toList());
factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().toList());
return factory;
}

}
// 其他容器...
}
  • TomcatServletWebServerFactory工厂类中,具体的创建方法:
@Override
// 获取web服务器
public WebServer getWebServer(ServletContextInitializer...initializers){
if(this.disableMBeanRegistry){
Registry.disableRegistry();
}
// 创建Tomcat容器
Tomcat tomcat=new Tomcat();
File baseDir=(this.baseDirectory!=null)?this.baseDirectory:createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
for(LifecycleListener listener:this.serverLifecycleListeners){
tomcat.getServer().addLifecycleListener(listener);
}
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);
}

Tomcat容器创建时机

SpringApplication.java

public ConfigurableApplicationContext run(String...args){
// ...
refreshContext(context); // 刷新上下文
afterRefresh(context,applicationArguments);
// ...
}

// ...

//////////////////////////////////////////////
// 刷新上下文
private void refreshContext(ConfigurableApplicationContext context){
if(this.registerShutdownHook){
shutdownHook.registerApplicationContext(context);
}
// 刷新
refresh(context);
}
//////////////////////////////////////////////
protected void refresh(ConfigurableApplicationContext applicationContext){
applicationContext.refresh();
}

web场景下,
ServletWebServerApplicationContext.java

@Override
public final void refresh()throws BeansException,IllegalStateException{
try{
// 刷新
super.refresh();
}
catch(RuntimeException ex){
WebServer webServer=this.webServer;
if(webServer!=null){
webServer.stop();
}
throw ex;
}
}

AbstractApplicationContext.java

@Override
// 创建容器十二步
public void refresh()throws BeansException,IllegalStateException{
synchronized (this.startupShutdownMonitor){
StartupStep contextRefresh=this.applicationStartup.start("spring.context.refresh");

// 1.准备上下文内容
// Prepare this context for refreshing.
prepareRefresh();

// 2.获取刷新的bean工厂
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory=obtainFreshBeanFactory();

// 3.准备上下文用到的bean工厂,如:类加载器,后置处理器
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try{
// 4.通过编程方式修改BeanFactory的配置,比如添加自定义的BeanDefinition,修改属性值,注册BeanPostProcessor等等。
// 通过这种方式,开发人员可以对Spring容器进行更灵活和定制化的配置
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// 5.执行所有已注册的BeanFactoryPostProcessor的postProcessBeanFactory方法
StartupStep beanPostProcess=this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// 6.注册所有的BeanPostProcessor实例.
// 允许开发人员在bean实例化和依赖注入的过程中对bean进行增强或定制
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();

// 7.初始化上下文的消息资源
// 消息资源用于国际化和本地化的目的,它可以根据不同的语言和区域设置,提供相应的文本消息
// Initialize message source for this context.
initMessageSource();

// 8.初始化应用程序事件的多播器,使得应用程序能够对事件进行发布和监听
// 事件机制是一种通信机制,用于在不同的组件之间传递消息和触发相应的处理逻辑
// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// 9.容器刷新过程中的一个回调方法,用于提供一个扩展点,让开发人员可以在容器刷新完成后执行一些自定义的逻辑
// 可以在onRefresh方法中执行一些额外的初始化操作、启动定时任务、注册额外的bean等
// Initialize other special beans in specific context subclasses.
onRefresh();

// 10.向应用程序上下文注册事件监听器
// Check for listener beans and register them.
registerListeners();

// 11.在Bean工厂初始化的最后阶段,完成所有注册的Bean的初始化过程
// Bean工厂中所有已注册的Bean的名称 -->
// 遍历所有的Bean名称获取对应的Bean定义 -->
// 根据Bean定义的信息,进行实例化、依赖注入、初始化等操作 -->
// 若Bean定义中有初始化方法(例如通过@PostConstruct注解标记的方法),则调用该方法进行额外的初始化逻辑
// 若Bean定义中有销毁方法(例如通过@PreDestroy注解标记的方法),则在容器关闭时调用该方法进行资源释放等操作
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// 12.容器刷新的最后阶段,执行一些额外的逻辑以完成刷新过程
// 清理初始化过程中一系列操作使用到的资源缓存
// 初始化LifecycleProcessor
// 启动所有实现了Lifecycle接口的bean
// 发布ContextRefreshedEvent事件
// Last step: publish corresponding event.
finishRefresh();
}

catch(BeansException ex){
if(logger.isWarnEnabled()){
logger.warn("Exception encountered during context initialization - "+
"cancelling refresh attempt: "+ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally{
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}

切换服务器

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>