Spring Boot源码阅读系列(3)-内嵌容器

137 阅读7分钟

引言

根据对RefreshContext(...)的解析,我们发现对内嵌容器的实现主要在onRefresh()中,

而在onRefresh()之前,我们已经进行了自动装载,并根据装载成功将所需的类加载到spring IOC容器中。Spring Boot就是根据IOC中的内容进行内嵌容器的初始化的。

嵌入式容器的自动装载

@AutoConfiguration(after = SslAutoConfiguration.class)
@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 {

    @Bean
    // 会在 postprocessors 时触发
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
          ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
          ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers, ObjectProvider<SslBundles> sslBundles) {
       return new ServletWebServerFactoryCustomizer(serverProperties, webListenerRegistrars.orderedStream().toList(),
             cookieSameSiteSuppliers.orderedStream().toList(), sslBundles.getIfAvailable());
    }

    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    // 会在 postprocessors 时触发
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
          ServerProperties serverProperties) {
       return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
    @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
    static class ForwardedHeaderFilterConfiguration {

       @Bean
       @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
       ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) {
          return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());
       }

       @Bean
       FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(
             ObjectProvider<ForwardedHeaderFilterCustomizer> customizerProvider) {
          ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
          customizerProvider.ifAvailable((customizer) -> customizer.customize(filter));
          FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
          registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
          registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
          return registration;
       }

    }

    interface ForwardedHeaderFilterCustomizer {
       void customize(ForwardedHeaderFilter filter);
    }

    // ......

}
  1. @AutoConfigureOrder这个注解是决定配置类的加载顺序的,当注解里的值越小越先加载,而Ordered.HIGHEST_PRECEDENCE的值是Integer.MIN_VALUE也就是说这个类肯定是最先加载的那一批
  2. @EnableConfigurationProperties开启ServerProperties类的属性值配置。而这个类里面包含的就是Web服务的配置
  3. @Import则又导入了对应的PostProcessorsRegistrar以及嵌入式容器

PostProcessorRegistrar

/**
 * 注册 WebServerFactoryCustomizerBeanPostProcessor,
 * 此类会在 early registration阶段通过 Import BeanDefinitionRegistrar注册
 */
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

   private ConfigurableListableBeanFactory beanFactory;
   @Override
   public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      if (beanFactory instanceof ConfigurableListableBeanFactory listableBeanFactory) {
         this.beanFactory = listableBeanFactory;
      }
   }

   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
         BeanDefinitionRegistry registry) {
      if (this.beanFactory == null) {
         return;
      }
      registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
            WebServerFactoryCustomizerBeanPostProcessor.class);
      registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
            ErrorPageRegistrarBeanPostProcessor.class);
   }

   private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,
         Class<T> beanClass) {
      if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
         RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
         beanDefinition.setSynthetic(true);
         registry.registerBeanDefinition(name, beanDefinition);
      }
   }
}

这个类注册了两个bean:WebServerFactoryCustomizerBeanPostProcessorErrorPageRegistrarBeanPostProcessor

/**
 * 该 BeanPostProcessor 会应用将所有的 WebServerFactoryCustomizer,
 * 应用于 WebServerFactory
 */
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {

    private ListableBeanFactory beanFactory;

    private List<WebServerFactoryCustomizer<?>> customizers;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
       Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
             "WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");
       this.beanFactory = (ListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
       if (bean instanceof WebServerFactory webServerFactory) {
          // System.out.println("run: " + beanName);
          postProcessBeforeInitialization(webServerFactory);
       }
       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;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
       return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
    }
}

run: tomcatServletWebServerFactory

这个 processor 的作用是获得所有定制器,然后执行定制器的方法, 在初始化阶段应用于 WebServerFactory,其中就包括 ServletWebServerFactoryAutoConfiguration中的ServletWebServerFactoryCustomizer,TomcatServletWebServerFactoryCustomizer

嵌入式Tomcat

@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加入到了IOC容器。

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    /**
     * Server HTTP port.
     */
    private Integer port;
    /**
     * Network address to which the server should bind.
     */
    private InetAddress address;
    
    // ......
}

这也是为什么我们平时使用server.port=80可以修改服务器的端口。spring boot在创建了服务器工厂之后,会通过postprocessors将属性写入工厂

createWebServer()

服务器的创建是在onRefresh()中进行的

下文是对此方法的解析。

@Override
protected void onRefresh() {
    super.onRefresh();
    try {
       createWebServer();
    }
    catch (Throwable ex) {
       throw new ApplicationContextException("Unable to start web server", ex);
    }
}

super.onRefresh()

不重要

/**
 * Initialize the theme capability.
 */
@Override
protected void onRefresh() {
    this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}

createWebServer()

ServletWebServerApplicationContext#createWebServer()

private void createWebServer() {
    // 获取当前是否存在webServer
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    // 什么都不存在
    if (webServer == null && servletContext == null) {
       // 服务器开始创建
       StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create");
       // 获得 WebServer 的创建工厂
       ServletWebServerFactory factory = getWebServerFactory();
       // 记录创建过程使用的工厂
       createWebServer.tag("factory", factory.getClass().toString());
       // 创建 webServer
       this.webServer = factory.getWebServer(getSelfInitializer());
       // 服务器创建结束
       createWebServer.end();
       getBeanFactory().registerSingleton("webServerGracefulShutdown",
             new WebServerGracefulShutdownLifecycle(this.webServer));
       getBeanFactory().registerSingleton("webServerStartStop",
             new WebServerStartStopLifecycle(this, this.webServer));
    }
    // 存在服务器,但不存在 servletContext
    else if (servletContext != null) {
       try {
          getSelfInitializer().onStartup(servletContext);
       }
       catch (ServletException ex) {
          throw new ApplicationContextException("Cannot initialize servlet context", ex);
       }
    }
    // 初始化属性源
    initPropertySources();
}

该方法中的主要语句为两句,1. 获得工厂,2. 工厂生产产品

// 获得 WebServer 的创建工厂
ServletWebServerFactory factory = getWebServerFactory();
// 创建 webServer
this.webServer = factory.getWebServer(getSelfInitializer());

getWebServerFactory()

ServletWebServerApplicationContext#getWebServerFactory()

/**
 * 返回一个ServletWebServerFactory,该类用于创建一个嵌入式WebServer。默认情况下,该方法会
 * 在context搜索一个合适的bean
 * 返回:一个webserver工厂,不会为null
 */
protected ServletWebServerFactory getWebServerFactory() {
    // Use bean names so that we don't consider the hierarchy
    // 从 applicationContext 中获得 ServletWebServerFactory 类的名字
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    if (beanNames.length == 0) {
       // 异常:找不到要使用的服务器,
       throw new MissingWebServerFactoryBeanException(getClass(), ServletWebServerFactory.class,
             WebApplicationType.SERVLET);
    }
    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);
}

image.png

根据调试内容,我们发现当前应用加载的是tomcatServletWebServerFactory,而这个bean是否非常眼熟,这个bean自动装载阶段Spring Boot加入到IOC容器中的。该工厂经过了postProcessor,来配置了属性。实际上,我们如果想在WebServerFactory中做一些属性操作,可以自己实现一些postProcessor

factory.getWebServer(...)

TomcatServletWebServerFactory#getWebServer(...)

有了工厂,现在使用工厂来创建WebServer。实际上,Tomcat本身就支持嵌入式启动,所以这里主要是将tomcat嵌入式启动可做的操作给做一遍

image.png

@Override
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);
    }
    // 为tomcat配置连接器,默认http11Nio
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    // 自定义连接器
    customizeConnector(connector);
    tomcat.setConnector(connector);
    // 注册连接器对应的执行器
    registerConnectorExecutor(tomcat, connector);
    tomcat.getHost().setAutoDeploy(false);
    // 配置引擎
    configureEngine(tomcat.getEngine());
    // 如果有额外的连接器
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
       tomcat.getService().addConnector(additionalConnector);
       registerConnectorExecutor(tomcat, additionalConnector);
    }
    // 配置上下文,通过initializers处理tomcat的context
    prepareContext(tomcat.getHost(), initializers);
    // 创建服务器,主要是封装tomcat
    return getTomcatWebServer(tomcat);
}

下面的函数prepareContext会对Context进行set设置,之后进行configureContext

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    // 返回doc_root的根路径
    File documentRoot = getValidDocumentRoot();
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    if (documentRoot != null) {
       context.setResources(new LoaderHidingResourceRoot(context));
    }
    context.setName(getContextPath());  // ""
    context.setDisplayName(getDisplayName());  // application
    context.setPath(getContextPath()); // ""
    File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
    context.setDocBase(docBase.getAbsolutePath());
    // 添加生命周期监听器
    context.addLifecycleListener(new FixContextListener());
    ClassLoader parentClassLoader = (this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
          : ClassUtils.getDefaultClassLoader();
    context.setParentClassLoader(parentClassLoader);
    // 默认的地域与字符集映射;例:JAPANESE -> UTF-8
    resetDefaultLocaleMapping(context);
    // 添加本地的一些映射关系
    addLocaleMappings(context);
    try {
       context.setCreateUploadTargets(true);
    }
    catch (NoSuchMethodError ex) {
       // Tomcat is < 8.5.39. Continue.
    }
    configureTldPatterns(context);
    // webapploader
    WebappLoader loader = new WebappLoader();
    loader.setLoaderInstance(new TomcatEmbeddedWebappClassLoader(parentClassLoader));
    loader.setDelegate(true);
    context.setLoader(loader);
    // 如果有默认servlet,则添加
    if (isRegisterDefaultServlet()) {
       addDefaultServlet(context);
    }
    // 如果有jsp,则添加
    if (shouldRegisterJspServlet()) {
       addJspServlet(context);
       addJasperInitializer(context);
    }
    context.addLifecycleListener(new StaticResourceConfigurer(context));
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    host.addChild(context);
    // 配置tomcat的上下文,主要是配置初始化器
    configureContext(context, initializersToUse);
    // 后处理, 空的
    postProcessContext(context);
}

prepareContext中,比较重要的操作是configureContext(...)主要是设置initializers

/**
 * 配置tomcat的context
 * @param context the Tomcat context
 * @param initializers initializers to apply
 */
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
    // 封装init,starter会在 tomcat 启动时触发
    TomcatStarter starter = new TomcatStarter(initializers);
    if (context instanceof TomcatEmbeddedContext embeddedContext) {
       embeddedContext.setStarter(starter);
       embeddedContext.setFailCtxIfServletStartFails(true);
    }
    // 添加容器初始化器,很重要,涉及到springboot与tomcat的交互
    context.addServletContainerInitializer(starter, NO_CLASSES);
    for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
       context.addLifecycleListener(lifecycleListener);
    }
    for (Valve valve : this.contextValves) {
       context.getPipeline().addValve(valve);
    }
    // 当tomcat发生error时,触发errorPage
    for (ErrorPage errorPage : getErrorPages()) {
       org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
       tomcatErrorPage.setLocation(errorPage.getPath());  // error
       tomcatErrorPage.setErrorCode(errorPage.getStatusCode()); // 0
       tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
       context.addErrorPage(tomcatErrorPage);
    }
    setMimeMappings(context);
    // 配置cookie和session
    configureSession(context);
    configureCookieProcessor(context);
    new DisableReferenceClearingContextCustomizer().customize(context);
    for (String webListenerClassName : getWebListenerClassNames()) {
       context.addApplicationListener(webListenerClassName);
    }
    for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
       customizer.customize(context);
    }
}

image.png

其中最重要的初始化器如图所示。其会触发spring boot的ServletWebServerApplicationContext#selfInitialize,触发时机则为容器的初始化阶段,因为TomcatStarter实现了ServletContainerInitializer接口

image.png

selfInitialize函数中,Spring Boot会为其添加1个servlet3个filter1个listener

image.png

servlet: DispacherServlet,了解springmvc的应该知道他的重要性

filter:characterEncodingFilter、formContentFilter、requestContextFilter

listener:cglib动态生成的主类

image.png

image.png

getTomcatWebServer(...)

实际上直接,tomcat.start()就可以启动tomcat了,但spring boot依然为其包装了一层,以进行一些initialize()操作

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;
    initialize();
}
private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with " + getPortsDescription(false));
    synchronized (this.monitor) {
       try {
          // 设置实例化的名字
          addInstanceIdToEngineName(); 

          Context context = findContext();
          context.addLifecycleListener((event) -> {
             if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                // Remove service connectors so that protocol binding doesn't
                // happen when the service is started.
                removeServiceConnectors();
             }
          });

          // 关闭绑定
          disableBindOnInit();  

          // 启动服务器,进入apache.tomcat包执行。会去执行初始化
          // 所做的初始化操作在上面说过了
          this.tomcat.start(); 

          // We can re-throw failure exception directly in the main thread
          rethrowDeferredStartupExceptions();

          try {
             ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
          }
          catch (NamingException ex) {
             // Naming is not enabled. Continue
          }

          // Unlike Jetty, all Tomcat threads are daemon threads. We create a
          // blocking non-daemon to stop immediate shutdown
          // 创建阻塞非守护线程防止程序关闭
          startNonDaemonAwaitThread();  
       }
       catch (Exception ex) {
          stopSilently();
          destroySilently();
          throw new WebServerException("Unable to start embedded Tomcat", ex);
       }
    }
}

总结

在处理嵌入式容器阶段,SpringBoot主要是

  1. 通过AutoConfigration加载对应的Tomcat工厂以及PostProcessorsRegistrar(Registrar会引入 BeanPostProcessor)
  2. 实例化容器工厂时,会通过BeanPostProcessor对工厂进行Customizer(自定义)
  3. 通过工厂创建tomcat,除了一些配置操作,最重要的是设置Tomcat的初始化器,在Tomcat启动阶段将DispacherServlet作为servlet加载进去

其中第1步由自动装载触发,也就是run方法中的invokeBeanFactoryPostProcessors(beanFactory);

第2,3步,在run方法中的onRefresh()触发。