切换语言为:繁体
SpringBoot的嵌入式Web服务器实现原理

SpringBoot的嵌入式Web服务器实现原理

  • 爱糖宝
  • 2024-07-24
  • 2081
  • 0
  • 0

我们SpringBoot应用都是通过SpringApplication.run()这一行代码启动起来的,那么我们有理由怀疑实现逻辑就在这个里面

应用跑起来-run()

 public ConfigurableApplicationContext run(String... args) {
 
     // 省略代码
     ...
         
     ConfigurableApplicationContext context = null;
 
     // 省略代码
     ...
     try {
         ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
         // 准备上下文环境
         ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
         // 配置忽略的Bean信息
         configureIgnoreBeanInfo(environment);
         // 打印Banner信息
         Banner printedBanner = printBanner(environment);
         // 创建ioc容器
         context = createApplicationContext();
         // 准备ioc容器【核心】
         prepareContext(context, environment, listeners, applicationArguments, printedBanner);
         // 刷新ioc容器
         refreshContext(context);
 
         // 省略代码
         ...
     }
 
     // 省略代码
     ...
     return context;
 }

刷新ioc容器-refreshContext()

 private void refreshContext(ConfigurableApplicationContext context) {
     if (this.registerShutdownHook) {
         try {
             context.registerShutdownHook();
         }
         catch (AccessControlException ex) {
             // Not allowed in some environments.
         }
     }
     // 进一步调用 refresh()
     refresh((ApplicationContext) context);
 }
 
 protected void refresh(ApplicationContext applicationContext) {
     Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
     // 进一步调用 refresh()
     refresh((ConfigurableApplicationContext) applicationContext);
 }
 
 // 调用 ConfigurableApplicationContext 的refresh()
 protected void refresh(ConfigurableApplicationContext applicationContext) {
     applicationContext.refresh();
 }

我们可以发现最终调用了ConfigurableApplicationContextrefresh()来完成刷新容器的操作

ServletWebServerApplicationContext

 public final void refresh() throws BeansException, IllegalStateException {
     try {
         // 调用父类的 refresh() 
         super.refresh();
     }
     catch (RuntimeException ex) {
         // 如果出现异常,并且web服务器创建好了,就会停止当前web服务器
         WebServer webServer = this.webServer;
         if (webServer != null) {
             webServer.stop();
         }
         throw ex;
     }
 }

AbstractApplicationContext

Spring容器刷新经典12大步

 public void refresh() throws BeansException, IllegalStateException {
     synchronized (this.startupShutdownMonitor) {
         // Prepare this context for refreshing.
         // 1、准备刷新
         prepareRefresh();
 
         // Tell the subclass to refresh the internal bean factory.
         // 2、获取一个BeanFactory
         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
         // Prepare the bean factory for use in this context.
         // 3、准备BeanFactory
         prepareBeanFactory(beanFactory);
 
         try {
             // Allows post-processing of the bean factory in context subclasses.
             // 4、BeanFactory的后置处理【留给子类的扩展】
             postProcessBeanFactory(beanFactory);
 
             // Invoke factory processors registered as beans in the context.
             // 5、执行BeanFactory的后置处理器
             invokeBeanFactoryPostProcessors(beanFactory);
 
             // Register bean processors that intercept bean creation.
             // 6、注册Bean的后置处理器
             registerBeanPostProcessors(beanFactory);
 
             // Initialize message source for this context.
             // 7、初始化MessageSource【国际化相关】
             initMessageSource();
 
             // Initialize event multicaster for this context.
             // 8、初始化事件派发工具
             initApplicationEventMulticaster();
 
             // Initialize other special beans in specific context subclasses.
             // 9、刷新【留给子类的扩展点,重点关注】
             onRefresh();
 
             // Check for listener beans and register them.
             // 10、主备监听器
             registerListeners();
 
             // Instantiate all remaining (non-lazy-init) singletons.
             // 11、完成BeanFactory的初始化工作【实例化剩下的单实例Bean】
             finishBeanFactoryInitialization(beanFactory);
 
             // Last step: publish corresponding event.
             // 12、刷新完成【发布事件】
             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();
         }
     }
 }

我们重点关注第9步 onRefresh(),因为这是留给子类的扩展点

子类ioc容器的扩展点-onRefresh()

ServletWebServerApplicationContext

 protected void onRefresh() {
     // 1、调用父类的 onRefresh()
     super.onRefresh();
     try {
         // 2、创建Web服务器
         createWebServer();
     }
     catch (Throwable ex) {
         throw new ApplicationContextException("Unable to start web server", ex);
     }
 }

创建Web服务器-createWebServer()

这里拿到的服务器工厂默认是Tomcat的,所以会创建出Tomcat服务器的实例然后启动

因为我们引入的starter-web里默认引入的就是Tomcatjar包

 private void createWebServer() {
     // 1、拿到Web服务器【刚开始肯定是null,因为我们还没创建呢】
     WebServer webServer = this.webServer;
     // 2、拿到ServletContext【刚开始也是null】
     ServletContext servletContext = getServletContext();
     if (webServer == null && servletContext == null) {
         // 3、从容器中得到一个Web服务器的工厂【ServletWebServerFactory】
         ServletWebServerFactory factory = getWebServerFactory();
         // 4、使用工厂来创建Web服务器
         this.webServer = factory.getWebServer(getSelfInitializer());
         getBeanFactory().registerSingleton("webServerGracefulShutdown",
                                            new WebServerGracefulShutdownLifecycle(this.webServer));
         getBeanFactory().registerSingleton("webServerStartStop",
                                            new WebServerStartStopLifecycle(this, this.webServer));
     }
     else if (servletContext != null) {
         try {
             // 遍历所有的ServletContextInitializer调用其onStartup()来启动
             getSelfInitializer().onStartup(servletContext);
         }
         catch (ServletException ex) {
             throw new ApplicationContextException("Cannot initialize servlet context", ex);
         }
     }
     initPropertySources();
 }

SpringBoot的嵌入式Web服务器实现原理

getWebServer()

 public WebServer getWebServer(ServletContextInitializer... initializers) {
     if (this.disableMBeanRegistry) {
         Registry.disableRegistry();
     }
     // 1、直接创建一个Tomcat服务器
     Tomcat tomcat = new Tomcat();
     // 2、设置一些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);
     // 3、得到一个Tomcat服务器【会启动Tomcat服务器】
     return getTomcatWebServer(tomcat);
 }
 
 protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
     return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
 }

创建一个TomcatWebServer,用来封装Tomat实例

 public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
     Assert.notNull(tomcat, "Tomcat Server must not be null");
     this.tomcat = tomcat;
     this.autoStart = autoStart;
     this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
     // 初始化:启动Tomcat
     initialize();
 }
 
 private void initialize() throws WebServerException {
     logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
     synchronized (this.monitor) {
         try {
 
             // 省略代码
             ...
 
             // 启动tomcat
             this.tomcat.start();
 
             // 省略代码
             ...
         }
         catch (Exception ex) {
             // 出现异常就停止、销毁Tomcat
             stopSilently();
             destroySilently();
             throw new WebServerException("Unable to start embedded Tomcat", ex);
         }
     }
 }

到这里Tomcat就创建并启动成功了。

如何切换Web服务器

案例:切换Web服务器为Jetty。

我们知道了内嵌服务器的原理,它是通过不同的Web服务器工厂来创建出不同的Web服务器实例,然后启动

Web服务器工厂通过条件注解进行注册到容器中,然后从容器中拿到Web服务器工厂,也就是导入不同的jar包就会注册不同的Web服务器工厂。所以我们只需要把tomcat的jar包替换为jetty的jar包即可完成切换。

Tomcat的jar包是在starter-web中引入的:

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-tomcat</artifactId>
     <version>2.3.12.RELEASE</version>
     <scope>compile</scope>
 </dependency>

我们只需要把这个jar包排除,然后加上jetty的jar包即可实现,SpringBoot帮我们把一切都在底层配置好了

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
     <exclusions>
         <exclusion>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-tomcat</artifactId>
         </exclusion>
     </exclusions>
 </dependency>
 
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-jetty</artifactId>
 </dependency>

再次启动,会发现已经切换成功了:

SpringBoot的嵌入式Web服务器实现原理

这里从容器中拿到的Web服务器工厂就是Jetty的了:

SpringBoot的嵌入式Web服务器实现原理

总结

我们发现内嵌Tomcat服务器的本质其实是:通过Web服务器工厂创建一个Tomcat实例,然后调用其start()来启动。

其它服务器也是一样的道理

     // 1、web服务器工厂创建服务器实例
     factory.getWebServer(getSelfInitializer())
     
     // 2、创建
     Tomcat tomcat = new Tomcat();
     
     // 启动
     tomcat.start();

Web服务器工厂如何被注册到容器中的?

使用ServletWebServerFactoryAutoConfiguration自动配置类

利用@Import()机制给容器中导入组件,然后通过@ConditonalOnxxx()条件注解给容器中导入不同的Web服务器工厂

     @Configuration(proxyBeanMethods = false)
     @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
     @ConditionalOnClass(ServletRequest.class)
     @ConditionalOnWebApplication(type = Type.SERVLET)
     @EnableConfigurationProperties(ServerProperties.class)
     @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
             ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
             ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
             ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
     public class ServletWebServerFactoryAutoConfiguration {
     }
     
     // 嵌入式的Tomcat
     @Configuration(proxyBeanMethods = false)
     @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
     @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
     static class EmbeddedTomcat {
         // Tomcat Web服务器工厂
         @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;
         }
     }
     
     // 嵌入式的Jetty
     @Configuration(proxyBeanMethods = false)
     @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
     @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
     static class EmbeddedJetty {
         // Jetty Web服务器工厂
         @Bean
         JettyServletWebServerFactory JettyServletWebServerFactory(
             ObjectProvider<JettyServerCustomizer> serverCustomizers) {
             JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
             factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
             return factory;
         }
     }
     
     // 嵌入式的Undertow
     @Configuration(proxyBeanMethods = false)
     @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
     @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
     static class EmbeddedUndertow {
         // Undertow Web服务器工厂
         @Bean
         UndertowServletWebServerFactory undertowServletWebServerFactory(
             ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
             ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
             UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
             factory.getDeploymentInfoCustomizers()
                 .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
             factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
             return factory;
         }
     }

WebServerFactory

用来创建Web服务器的工厂

SpringBoot的嵌入式Web服务器实现原理

WebServer

Web服务器

SpringBoot的嵌入式Web服务器实现原理

WebServer实际是调用具体的服务器实例来做的:Tomcat、Jetty、Undertow

public class Tomcat {}
     
public class Jetty {}

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.