关于springboot:SpringBoot成长记9onRefresh如何启动内嵌的Tomcat容器的

2次阅读

共计 17901 个字符,预计需要花费 45 分钟才能阅读完成。

上一节咱们次要剖析了 refreshContext 中,次要有 3 个逻辑,如下图:

上一节重点解析了 invokeBeanFactoryPostProcessors 执行容器扩大点,实现了主动配备配置、第三方执行扩大的执行。

明天咱们持续剖析 refreshContext 另一个重要的逻辑 onRefresh()逻辑,让咱们开始吧!

疾速概览:onRefresh 启动内嵌 tomcat 前的操作

refreshContext 中 onRefresh 之前还有一些逻辑,咱们先来疾速看下它们次要做了什么。首先来看下代码:

    @Override
    public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {
              // 省略

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

            // 省略
    }

下面次要波及了 3 个办法,从名字行就能猜出来它们做了什么:

1)registerBeanPostProcessors 通过扫描到 BeanDefination 中找出 BeanPostProcessor,减少几个 Bean 的扩大点 BeanPostProcessor 按 4 类程序一一减少。

回顾术语 BeanPostProcessor 是什么?

之前 BeanFactoryPostProcessor 是对容器的扩大,次要有一个办法,能够给容器设置属性,补充一些单例对象,补充一些 BeanDefinition。那 BeanPostProcessor 是对 bean 的扩大,有 before 和 after 两类办法,对 Bean 如何做扩大,在 bean 的创立前后,给 bean 补充一些属性等。

2)initMessageSource 注册音讯 M essageSource 对象到容器 DelegatingMessageSource 国际化相干反对,默认的没有。

3)initApplicationEventMulticaster 注册播送对象到容器 这个对象就是之前触发 listener 扩大点的播送对象。

相熟了 onRefresh 办法之前的大体逻辑后,目前为止,整个 rereshConext()执行的逻辑次要如下:

onRefresh 的外围脉络

相熟了 onRefresh 办法之前的大体逻辑后,接下来咱们就先钻研下 onRefresh 的外围脉络在做什么了。

    //ServletWebServerApplicationContext.java
    @Override
    protected void onRefresh() {super.onRefresh();
        try {createWebServer();
        }
        catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);
        }
    }
    // 父类 GenericWebApplicationContext.java
    @Override
    protected void onRefresh() {this.themeSource = UiApplicationContextUtils.initThemeSource(this);
    }

这个 onRefresh 办法的脉络其实很简略,父类没有什么逻辑,外围应该就是 createWebServer 了,咱们持续来看下:

    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {ServletWebServerFactory factory = getWebServerFactory();
            this.webServer = factory.getWebServer(getSelfInitializer());
        }
        else if (servletContext != null) {
            try {getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        initPropertySources();}

这个逻辑其实很有意思,次要的外围脉络是 if-else-if

1)如果 webServer 和 servletContext 为空,就创立一个 WebServer,之后执行 initPropertySources。

2)否则就应用 getSelfInitializer,执行 onStartup 办法,之后执行 initPropertySources。

能够默认状况 webServer 和 servletContext 为空的,这个咱们在之前剖析的整个流程中没看到过任何对于这两个组件的逻辑,或者你本人断点也能显著的找到代码执行的门路。

这里其实你会发现,判断走哪个分支的办法可能不止一种,你能够连蒙带猜,也能够断点走一下,也能够依据教训剖析等等。办法有很多,大家千万剖析原理或者源码的时候不要陷入追寻有哪些办法上。办法不是最重要的,适宜你本人的就好。这个思维很要害,你能够模拟,但不能齐全照搬,肯定要联合本人状况思考,最终能力成为你本人的。本人悟到的才是本人的,这些也是思维,也是最要害的。所以不要总问我有哪些办法,有时候是你本人悟出来的,我只能揭示或者倡议。

好了,言归正传,这里理论走的门路就是第一条了。如下图所示:

最终,你会发现 onRefresh 波及的外围组件ServletWebServerFactoryWebServerServletContext

SpringBoot 对 web 容器的形象封装和设计

既然之前波及到了几个组件ServletWebServerFactoryWebServerServletContext。那它们是别离是什么货色呢?

其实从名字就能猜出很多货色,不难想到:

ServletContext,这个是指解决整个 web 申请是的上下文对象,在 Tocmat 中通常是整个申请的上下文参数都封装在这个对象中了,十分要害的对象。

ServletWebServerFactory 和 WebServer 是什么?很显著 ServletWebServerFactory 是个工厂,用来创立 WebServer。

而 WebServer 从接口中定义的办法就可以看进去,封装了 web 容器的启动和进行,获取端口的外围操作,也就是说 WebServer 是 web 容器的一个形象封装。

@FunctionalInterface
public interface ServletWebServerFactory {

   /**
    * Gets a new fully configured but paused {@link WebServer} instance. Clients should
    * not be able to connect to the returned server until {@link WebServer#start()} is
    * called (which happens when the {@code ApplicationContext} has been fully
    * refreshed).
    * @param initializers {@link ServletContextInitializer}s that should be applied as
    * the server starts
    * @return a fully configured and started {@link WebServer}
    * @see WebServer#stop()
    */
   WebServer getWebServer(ServletContextInitializer... initializers);

}
public interface WebServer {

    /**
     * Starts the web server. Calling this method on an already started server has no
     * effect.
     * @throws WebServerException if the server cannot be started
     */
    void start() throws WebServerException;

    /**
     * Stops the web server. Calling this method on an already stopped server has no
     * effect.
     * @throws WebServerException if the server cannot be stopped
     */
    void stop() throws WebServerException;

    /**
     * Return the port this server is listening on.
     * @return the port (or -1 if none)
     */
    int getPort();}

从下面两个接口的设计和正文看

首先 ServletWebServerFactory 的 getWebServer 正文翻译:获取一个新的齐全配置但暂停的 {@link WebServer} 实例。客户应无奈连贯到返回的服务器,直到 {@link WebServer#start()} 是调用(当 {@code ApplicationContext} 已齐全刷新)。

也就是说,这个办法意思就是获取到一个配置好的 webServer 容器,在调用 start 办法时启动容器,启动时候 ApplicationContext,也就是 Spring 容器曾经实现了刷新。

WebServer 接口封装了 web 容器的常见操作,如启动、进行,获取端口号之类的。

也就是说 ServletWebServerFactory 能够取得一个 web 容器,WebServer 能够操作一个容器。

并且从上面的图中能够看出,它们有很多不同 web 容器的实现。整体如下图所示:

综上,最终你能够了解为 WebServer 和 ServletWebServerFactory,这一套,其实就是 SpringBoot 对 web 容器的形象封装,WebServer 能够代表了整个容器。

理解了 onRefesh 的整体脉络和要害的组件之后,咱们来看下如何创立 webServer 的。

默认状况咱们获取到的是TomcatServletWebServerFactory,通过它来创立

//TomcatServletWebServerFactory.java
public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();
        }
        Tomcat tomcat = new 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);
        return getTomcatWebServer(tomcat);
    }

整个办法脉络如下:

1)入参是一个 java8 定义的函数表达式,也就是参数传递进来了一个办法,应用的是函数式接口 ServletContextInitializer。这个办法在前面应该会被执行的。

2)创立了外围组件 Tomcat,一会能够看下它的外围脉络,外面封装了 Server

3)创立和配置组件 Connector,new Connector()、customizeConnector 这个是 Tomcat 的 Connector 组件相干

4)创立和配置组件 Engine getEngine、configureEngine tomcat 的 Engine 组件相干设置

5)prepareContext 筹备 tomcat 的 context 相干

6)getTomcatWebServer 真正启动 tomcat

画成图如下所示:

看完这个办法后,你可能对这里波及的很多组件比拟生疏,因为波及到了很多 tomcat 的组件。不过没有关系,你能够通过之前学习的办法来梳理这些组件的关系。就算不晓得每个组件是干嘛的,也能够连蒙带猜下。

new Tomcat 外围组件和脉络剖析

这里我就来带教大家一起用之前的办法和思路剖析一下它们的外围脉络吧。

首先第一个组件就是 Tomcat 这个类的创立。老办法,能够看下这个类脉络、构造方法之后,画一个图。

首先看下构造方法

    public Tomcat() {ExceptionUtils.preload();
    }

你会发现什么都么有,只有一个异样工具预加载的解决。一看就不是重点。

那就再看下这个类的整体脉络吧:

看完这个类的脉络,能够看进去 Tomcat 这个类次要有
1)对 Wrapper 相干的操作方法,比方 addServelt 办法就是返回一个 Wrapper。

2)有 Context 相干一些办法,createContext、addWebapp 之类的

3)有一堆组件的 get 办法,比方 Connector、Engine、Service、Server、Host

4)最初就是一些属性了,比方 Server 对象、端口号、hostname、用户权限相干 Userpass/UserRole 之类的。

尽管咱们不晓得这个类外面的那些组件是干嘛的。然而起码咱们有了一个印象。能够感触到这个 Tomcat 类,封装了简直 Tomcat 所有的外围组件,是一个对 tomcat 容器的一个形象对象,代表了整个 tomcat。

最初咱们能够画图先列举下看完 Tomcat 这个类的构造函数和类脉络中,次要波及概念或者说是组件,如下图所示:

不晓得你们目前是什么感触,感觉有好多新的概念。如果你不理解 tomcat 的原理的话,第一次看到这一大堆组件,必定有点懵的。

不过没关系,你其实能够连蒙带猜,或者抓大放小,因为咱们次要还是看 SpringBoot 如何启动内嵌 tomcat,如何和 tomcat 整合 Spring 容器的。

所以你没必要非要弄清楚这些组件,等之后咱们 Tomcat 成长记,钻研 tomcat 的原理和源码时候再来认真弄清楚。

这里咱们还是找到关注的重点就能够了。

好,咱们接着向下剖析。

Connector 根本创立和扩大设计

最高层的形象封装 Tomcat 对象创立实现后,下一个外围创立的就是 Connector 了。创立它的代码如下:

public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";

private String protocol = DEFAULT_PROTOCOL;    

public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 其余
    Tomcat tomcat = new Tomcat();
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    // 其余
 }

能够 看到 Connector 创立默认传入了一个 Http11NioProtocol 类的全名,当然你能够通过 set 办法批改这个 protocol 的默认值,只有获取到 TomcatServletWebServerFactory 就能够批改对吧?

至于为啥传递了类的全名,你猜测下都晓得,它外部可能是通过反射创立了这个类,并把这个组件设置给了 Connector。咱们来看下是不是:

public Connector(String protocol) {boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
            AprLifecycleListener.getUseAprConnector();

    if ("HTTP/1.1".equals(protocol) || protocol == null) {if (aprConnector) {protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";} else {protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";}
    } else if ("AJP/1.3".equals(protocol)) {if (aprConnector) {protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";} else {protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";}
    } else {protocolHandlerClassName = protocol;}

    // Instantiate protocol handler
    ProtocolHandler p = null;
    try {Class<?> clazz = Class.forName(protocolHandlerClassName);
        p = (ProtocolHandler) clazz.getConstructor().newInstance();
    } catch (Exception e) {
        log.error(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"), e);
    } finally {this.protocolHandler = p;}

    // Default for Connector depends on this system property
    setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}

这个构造函数最要害的就是 3 行代码:

public Connector(String protocol) {Class<?> clazz = Class.forName(protocolHandlerClassName);
  p = (ProtocolHandler) clazz.getConstructor().newInstance();
  this.protocolHandler = p;
 }

也就是说其实 new Connector 外围就做了一件事件:创立了一个 Http11NioProtocol 组件。

这个从名字上看就是一个 NIO 相干的通信组件,外部应该会有 Selector、Channel、Bytebuffer 等 NIO 外围组件的。

至于 Http11NioProtocol 如何创立的这里我就不带大家深究了,你能够剖析它的构造函数、类脉络、画一个组件图剖析下它的创立过程,或者之后咱们 Tomcat 成长记会详细分析的,可之后带大家一起剖析下。

到这里先画个图小结下:

new Connector 之后就是十分要害的扩大点执行了 customizeConnector() 办法。

这个办法理论是 SpringBoot 对 Connector 扩大设计的接入,能够批改 Connector 中很多配置和属性,让咱们来一起看下。

private Set<TomcatConnectorCustomizer> tomcatConnectorCustomizers = new LinkedHashSet<>();

private Set<TomcatProtocolHandlerCustomizer<?>> tomcatProtocolHandlerCustomizers = new LinkedHashSet<>();

protected void customizeConnector(Connector connector) {int port = Math.max(getPort(), 0);
        connector.setPort(port);
        if (StringUtils.hasText(this.getServerHeader())) {connector.setAttribute("server", this.getServerHeader());
        }
        if (connector.getProtocolHandler() instanceof AbstractProtocol) {customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
        }
        invokeProtocolHandlerCustomizers(connector.getProtocolHandler());
        if (getUriEncoding() != null) {connector.setURIEncoding(getUriEncoding().name());
        }
        // Don't bind to the socket prematurely if ApplicationContext is slow to start
        connector.setProperty("bindOnInit", "false");
        if (getSsl() != null && getSsl().isEnabled()) {customizeSsl(connector);
        }
        TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
        compression.customize(connector);
        for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {customizer.customize(connector);
        }
}

这个扩大办法外围的逻辑就是在给 connector 进行一些属性设置,外围通过了两个扩大进行调用。

1)invokeProtocolHandlerCustomizers 执行对 ProtocolHandler 扩大

2)customizer.customize(connector); 执行对 Connector 的扩大

其实能够看到触发的都是 tomcatConnectorCustomizers、tomcatProtocolHandlerCustomizers 这两个汇合中的扩大类。、

整体如下图所示:

咱们先不焦急看这些扩大类做了什么,首先的得思考下,tomcatConnectorCustomizers、tomcatProtocolHandlerCustomizers 这两个汇合中的扩大类什么时候设置的值呢?

其实你想下,这两个属性属于谁呢?没错,属于 TomcatServletWebServerFactory。而这个类是不是之前通过 ServletWebServerApplicationContext 执行 onRefresh 脉络时候获取到的呢?如下图:

对应获取的代码如下:

//ServletWebServerApplicationContext.java
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);
}

能够看到的是,这个办法外围就是通过 getBean 从容器获取一个对象。然而其实容器中并没有 ServletWebServerFactory 这个对象,只有它的 BeanDefinition。

为什么呢?因为之前咱们执行 ConfigurationClassPostProcessor 时候,只是加载了 Bean 对应的 BeanDefinition 而已。

不过没关系,getBean 中的逻辑是,如果容器没有然而有对应的 BeanDefinition,它会进行 Bean 实例化,Bean 的实例化咱们下一节会具体讲,这里只是简略提下。

那这个 bean,ServletWebServerFactory 实例化的时候会做什么呢?除了根本构造函数外,其实 Bean 实例化的过程有很多扩大点,能够为 bean 设置属性。

好,那要害的就来了,tomcatConnectorCustomizers、tomcatProtocolHandlerCustomizers 既然是 ServletWebServerFactory 的两个属性,必定就能够通过 Bean 实例化时候的扩大点,给这两个属性设置进去值。

最终,我给大家详情如下图所示:

至于 Connector 中每个 customizer 做了哪些事件,这里咱们不去详细分析了

大体就是初始化 protocol 相干的配置,比方 setMaxThreads 默认 200、minSpareThreads 默认 10、maxHttpHeaderSize 默认 8192byte、maxSwallowSize 2097152 等等。

相熟了这个扩大点的逻辑后,其实最要害的是如何应用它,你能够通过 ServerProperties 扩大配置值,也能够自定义 tomcatConnectorCustomizers 或者 tomcatProtocolHandlerCustomizers,只有实现对应的接口就能够了。这个才是领悟了 SpringBoot 的设计思路后最要害的。

术语遍及:Tomcat 的 Engine、Context、Host、Wrapper 关系

剖析完了 Connector 的创立之后,其余的组件其实就是一般的创立,建设关联关系而已。它们的关系其实不简单,属于 tomcat 的基本知识,这里我通过一个 tomcat 流程执行图给大家介绍下它们的关系即可。它们之间的关系如下图所示:

这些组件每个都有本人的职责,你大体理解上述组件的关系就能够了,咱们就不开展剖析了。

当然 SpringBoot 也有一些对它们的扩大,比方对 Engine、Context 阀门的扩大。也是通过 engineValves、contextValves 两个 list 属性进行扩大。

//TomcatServletWebServerFactory.java
private List<Valve> engineValves = new ArrayList<>();

private List<Valve> contextValves = new ArrayList<>();

只不过这两个汇合默认是空的,你能够通过 TomcatServletWebServerFactory 对他们进行设置和扩大。

这里我也不开展了。

记住,只有你了解了 SpringBoot 围绕 TomcatServletWebServerFactory 对 tomcat 做封装和扩大是要害,就能够了。

prepareContext 中的扩大点 ServletContextInitializer

后面一堆组件创立实现后,还有一个比拟有意思的操作就是 prepareContext。

让咱们来看下吧!它的代码如下:

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {File documentRoot = getValidDocumentRoot();
   TomcatEmbeddedContext context = new TomcatEmbeddedContext();
   if (documentRoot != null) {context.setResources(new LoaderHidingResourceRoot(context));
   }
   context.setName(getContextPath());
   context.setDisplayName(getDisplayName());
   context.setPath(getContextPath());
   File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
   context.setDocBase(docBase.getAbsolutePath());
   context.addLifecycleListener(new FixContextListener());
   context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
         : ClassUtils.getDefaultClassLoader());
   resetDefaultLocaleMapping(context);
   addLocaleMappings(context);
   context.setUseRelativeRedirects(false);
   try {context.setCreateUploadTargets(true);
   }
   catch (NoSuchMethodError ex) {// Tomcat is < 8.5.39. Continue.}
   configureTldSkipPatterns(context);
   WebappLoader loader = new WebappLoader(context.getParentClassLoader());
   loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
   loader.setDelegate(true);
   context.setLoader(loader);
   if (isRegisterDefaultServlet()) {addDefaultServlet(context);
   }
   if (shouldRegisterJspServlet()) {addJspServlet(context);
      addJasperInitializer(context);
   }
   context.addLifecycleListener(new StaticResourceConfigurer(context));
   ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
   host.addChild(context);
   configureContext(context, initializersToUse);
   postProcessContext(context);
}

整个办法的脉络其实不简单, 次要就是:

1)new TomcatEmbeddedContext

2)为 tomcat 的这个 Context 设置了很多值

3)执行了一个扩大点 ServletContextInitializer

整体如下图所示:

至于扩大点,ServletContextInitializer 执行了什么?

其实能够看下它的逻辑。它是应用了 java8 的个性,通过一个函数式接口传入过去的办法,也就是说,通过办法参数传递过去了一个行为,而不是一个变量。

咱们能够找到传入的地位:

//ServletWebServerApplicationContext.java
    private void selfInitialize(ServletContext servletContext) throws ServletException {prepareWebApplicationContext(servletContext);
        registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {beans.onStartup(servletContext);
        }
    }

办法其实不简单,外围就是触发了 ServletContextInitializer 的所有实现,执行了扩大办法 onStartup。

默认次要有 4 个实现:

result = {ServletContextInitializerBeans@6345}  size = 4
 0 = {DispatcherServletRegistrationBean@6339} "dispatcherServlet urls=[/]"
 1 = {FilterRegistrationBean@6350} "characterEncodingFilter urls=[/*] order=-2147483648"
 2 = {FilterRegistrationBean@6351} "formContentFilter urls=[/*] order=-9900"
 3 = {FilterRegistrationBean@6352} "requestContextFilter urls=[/*] order=-105"

其实从名字就看进去了,它的含意是往 ServletContext 中注册一堆 Servelt、Filter 等等。

这个扩大点还是比拟要害的。

整体如下图所示:

思考:tomcat 和 SpringBoot 怎么整合的?

剖析完了整个 WebServer 的创立后,其实你就会发现:

最终是 Spring 的容器 ServletWebServerApplicationContext 创立了 WebServer,它持有了这对象,也就有了 Tomcat 整个形象封装。

天然它们就整合到了一起了。

内嵌 Tomcat 最终的启动

之前剖析的整个逻辑都是 webServer 这个对象的创立,之前从正文咱们就晓得,创立的 webServer 只是一个配置实现,进行的 web 容器,web 容器并没有启动。只有调用 webServer#start()这个办法,容器才会真正启动。

所以,最初咱们来剖析下 tomcat 是如何启动的。启动的代码就是 getWebServer 的最初一行,代码如下:

//TomcatServletWebServerFactory.java
    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        // 省略 Tomcat 的创立、connector 的创立和扩大、其余组件的创立、prepareContext 的执行和扩大
        return getTomcatWebServer(tomcat);
    }
    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {return new TomcatWebServer(tomcat, getPort() >= 0);
    }
    public TomcatWebServer(Tomcat tomcat, boolean autoStart) {Assert.notNull(tomcat, "Tomcat Server must not be null");
            this.tomcat = tomcat;
            this.autoStart = autoStart;
            initialize();}

能够看到下面的代码脉络是:通过一系列的办法调用最终将创立的 tomcat 对象,有封装了一下,封装为了 TomcatWebServer 对象, 之后执行了 initialize()。

private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s):" + 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();}
                });

                // Start the server to trigger initialization listeners
                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
                startDaemonAwaitThread();}
            catch (Exception ex) {stopSilently();
                destroySilently();
                throw new WebServerException("Unable to start embedded Tomcat", ex);
            }
        }
    }

下面办法逻辑看似多,其实最要害的就一句话。这里外围是你抓大放小,次要关注一句话就能够了:

tomcat.start();

这个 start 办法执行的流程很有意思。它是相似一个链式调用。

其实你从之前 tomcat 的组件图就能够猜到,它们组件层级关系很多,每个组件都会触发下一层组件的逻辑。

每个组件都有生命周期,比方 init 办法 –>start()–>destory()之类的。

那么也就说 tomcat 会以链的形式逐级调用各个模块的 init()办法进行初始化, 待各个模块都初始化后, 又会逐级调用各个模块的 start()办法启动各个模块。

整体大略如下图所示:

小结

最初咱们小结下,明天咱们次要剖析了 SpringBoot 在 onRefresh 办法中如何启动的 tomcat:

1)疾速该来了 onRefresh 启动内嵌 tomcat 前的操作

2)剖析了 onRefresh 的外围脉络

3)思考了 SpringBoot 对 web 容器的形象封装和设计

4)对 new Tomcat 进行了外围组件和脉络剖析

5)剖析了 Connector 根本创立和扩大设计

6)术语遍及:Tomcat 的 Engine、Context、Host、Wrapper 关系

7)prepareContext 中的扩大点 ServletContextInitializer

8)思考了:tomcat 和 SpringBoot 怎么整合的?

9)内嵌 Tomcat 最终的启动

最初补充一点思维:

整个过程中有的是知识点,有的是一些设计的思考、扩大点的思考。大家肯定要学会抓重点。这个能力十分要害。

这就是波及到了一个能力模型的档次,能够有这样一种划分

第一个档次:常识、技术根本的应用,也就是说咱们从理解常识到应用它做好一件事是一个档次。这个能够体现在你学习技术上,率领团队做我的项目,或者学习任何新事物上。

第二个档次:通过思考和总结,形象和提炼出事件的关键点。比方一个提炼设计思维,发现我的项目的要害节点、门路等。

最初一个档次:站在肯定高度和视角,掌控和推动整体。这个就须要很多教训和像比你胜利的人学习了,因为把握力是能够练习的,然而视线和思维的高度,尽管能够教训积攒,然而最快的形式就是像比你优良的人学习,如果有一个导师的话那就更好了。

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0