spring boot是如何加载Tomcat的

大家都用过spring boot,都知道他启动的时候内置一个Tomcat,但是他是怎么来的,你们知道吗

这篇博客将带你们走进spring boot的源码,看看spring boot底层是如何加载tomcat的,走起发车!

1. 方法入口

入口SpringApplication.run()这个方法大家都很熟悉我就不细讲了,我们从这进去一直找到public ConfigurableApplicationContext run(String... args),然后找到refreshContext这个方法

public ConfigurableApplicationContext run(String... args) {
      //此处省略
 	  //入口方法
     this.refreshContext(context);
      //此处省略
}

继续往refresh这个方法找,一直跟着refresh找,直到找到ConfigurableApplicationContext的抽象类

private void refreshContext(ConfigurableApplicationContext context) {
    this.refresh((ApplicationContext)context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException var3) {
        }
    }

}

2. SpringContext上下文

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
  //此处省略
   //web容器上下文入口
   void refresh() throws BeansException, IllegalStateException;
   //此处省略

}

我们找到他的实现类,我们看到有三个对不对,这时候我们选择AbstractApplicationContext,不要选ServletWebServerApplicationContext,因为ServletWebServerApplicationContextAbstractApplicationContext的子类,我们先看他的抽象方法

spring boot是如何加载Tomcat的-LMLPHP

进入到AbstractApplicationContext中,我们重点看onRefresh方法

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        this.prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);

        try {
            this.postProcessBeanFactory(beanFactory);
            this.invokeBeanFactoryPostProcessors(beanFactory);
            this.registerBeanPostProcessors(beanFactory);
            this.initMessageSource();
            this.initApplicationEventMulticaster();
            //加载bean
            this.onRefresh();
            this.registerListeners();
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var9) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }

            this.destroyBeans();
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            this.resetCommonCaches();
        }

    }
}

点进去他是一个空实现

protected void onRefresh() throws BeansException {
}

我们 找到他的实现类ServletWebServerApplicationContext,这是加载web容器的上下文,我们可以明显看到createWebServer就是一个创建web容器的方法

protected void onRefresh() {
    super.onRefresh();

    try {
        //创建web容器
        this.createWebServer();
    } catch (Throwable var2) {
        throw new ApplicationContextException("Unable to start web server", var2);
    }
}

3. 创建web容器

我们进去源码继续看,有个getWebServerFactory方法,这是获取webserver工厂类的方法,相信我们离真相不远了

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = this.getServletContext();
    if (webServer == null && servletContext == null) {
        //获取webServerr工厂
        ServletWebServerFactory factory = this.getWebServerFactory();
        //通过工厂生成webServer
		this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
    }
    this.initPropertySources();
}

我们继续看可以看到其实我们是获取ServletWebServerFactory的bean,那ServletWebServerFactory具体有什么实现呢

protected ServletWebServerFactory getWebServerFactory() {
    String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    //此处省略
}

看到这是不是很清晰啦,我们直接找到TomcatServletWebServerFactory,看他的getWebServer实现
spring boot是如何加载Tomcat的-LMLPHP

很清晰的看到,这就是tomcat的配置,host,connector之类的,相信以前我们学tomcat的时候都见过

public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }

    Tomcat tomcat = new Tomcat();
    File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    this.customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    this.configureEngine(tomcat.getEngine());
    Iterator var5 = this.additionalTomcatConnectors.iterator();

    while(var5.hasNext()) {
        Connector additionalConnector = (Connector)var5.next();
        tomcat.getService().addConnector(additionalConnector);
    }

    this.prepareContext(tomcat.getHost(), initializers);
    //启动web容器
    return this.getTomcatWebServer(tomcat);
}

4. 启动web容器

tomcat是在上文中的getTomcatWebServer中启动的,我们再深入进去

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
}

这是一个TomcatWebServer的构造方法,我们再点进去

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
    //此处省略
    //初始化tomcat
    this.initialize();
}

进入initialize方法,不用说大家都知道tomcat.start就是启动tomcat啦

private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
    synchronized(this.monitor) {
        try {
            this.addInstanceIdToEngineName();
            Context context = this.findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource()) && "start".equals(event.getType())) {
                    this.removeServiceConnectors();
                }

            });
            // 启动tomcat
            this.tomcat.start();
            this.rethrowDeferredStartupExceptions();

            try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
            } catch (NamingException var5) {
            }

            this.startDaemonAwaitThread();
        } catch (Exception var6) {
            this.stopSilently();
            this.destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", var6);
        }

    }
}

结语

这篇文章下来,是不是感觉以前不理解的东西一下子就清晰了呢,这篇文章希望能帮助大家能更简单的去了解源码,学习总是快乐的嘿嘿

09-27 15:53