Spring Boot 内嵌 Tomcat 实现原理解析

前言

为什么 Spring Boot 不需要额外安装 Tomcat 了?

因为 Spring Boot 有内置的 Web 服务器 Tomcat,所以不用单独配置。

这篇文章主要从源码的角度出发,解析 Spring Boot 内嵌 Tomcat 的实现原理,讨论 Tomcat 何时创建、启动以及是怎样启动的。

下面跟随源码一步步找到如何启动内置的 tomcat。

首先贴出启动类:

@SpringBootApplication
public class QuartJobApplication {

    public static void main(String[] args) {
        SpringApplication.run(QuartJobApplication.class, args);
    }

}

我们点击 run 进入源代码:

    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

    public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
             .......省略.......
            this.refreshContext(context);
            ........省略.......

    }

上面省略部分的源码在我分享的上一篇文章中已经讲过,这里就不在重复了,感兴趣的小伙伴可以去看一下:

Spring Boot 启动流程原理分析

这里面我们主要关心的是 this.refreshContext(context); 这个方法,因为 Tomcat 就在这里面。

下面我们点击进去看一下

在这里插入图片描述
我们接着点击 refresh 方法

在这里插入图片描述

在这里插入图片描述
这里选择 ServletWebServerApplicationContext 的类

进入以下的方法以后选择点击 onRefresh

    public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            //1、调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
            this.prepareRefresh();
            //2、创建 beanFactory,主要是加载 bean 的信息,分为定位,加载,注册
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //3、为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
            this.prepareBeanFactory(beanFactory);

            try {
                 //4、BeanFactory 准备工作完成后进行的后置处理工作;
                 //抽象的方法,当前未做处理。子类通过重写这个方法来在 BeanFactory 创建并预准备完成以后做进一步的设置
                this.postProcessBeanFactory(beanFactory);
                //5、调用所有注册的 BeanFactoryPostProcessor的Bean
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //6、为 BeanFactory 注册 BeanPost 事件处理器.这是仅仅是注册,调用在 getbean 的时候
                this.registerBeanPostProcessors(beanFactory);
                //7、初始化 MessageSource 组件(做国际化功能;消息绑定,消息解析)
                this.initMessageSource();
                //8、初始化容器事件传播器
                this.initApplicationEventMulticaster();
                //9、调用子类的某些特殊 Bean 初始化方法(这里是我们主要看的)
                this.onRefresh();
                //10、为事件传播器注册事件监听器.
                this.registerListeners();
                //11、初始化所有剩余的单例 Bean
                this.finishBeanFactoryInitialization(beanFactory);
                //12、完成 BeanFactory 的初始化创建工作,IOC容器就创建完成
                this.finishRefresh();
            } catch (BeansException var10) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                }
                //13、销毁已创建的Bean
                this.destroyBeans();
                //14、取消refresh操作,重置容器的同步标识.
                this.cancelRefresh(var10);
                throw var10;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

上面每个方法的大致含义就是这样,下面我们选择 onRefresh 方法进去看源码:

在这里插入图片描述

这里选择 ServletWebServerApplicationContext 类里面的方法

在这里插入图片描述

点击 this.createWebServer的方法进去,源码如下:

    private void createWebServer() {
       //第一次过来,这个为 null
        WebServer webServer = this.webServer;
        //这里也为 null
        ServletContext servletContext = this.getServletContext();
        //第一次进来上面 webServer servletContext 都是null,会进到if分支里面
        if (webServer == null && servletContext == null) {
         //这里不需要关注,只是标记一下
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            //获取 Servlet 服务器工厂
            ServletWebServerFactory factory = this.getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
            //工厂方法,获取 Servlet 服务器,并作为 AbstractApplicationContext 的一个属性进行设置
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var5) {
                throw new ApplicationContextException("Cannot initialize servlet context", var5);
            }
        }
       //初始化一些ConfigurableEnvironment中的 ServletContext信息
        this.initPropertySources();
    }

我们选择 factory.getWebServer 点击进入看源码:

在这里插入图片描述

getWebServer 方法主要处理的是Tomcat 容器对象的创建、环境配置和启动。

    public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        //实例化 Tomcat,可以理解为 Server 组件
        Tomcat tomcat = new Tomcat();
        //创建一个Tomcat 临时文件路径
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        //创建连接协议,默认使用HTTP1.1协议,NIO网络模型
        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);
        }
        //初始化TomcatEmbeddedContext
        this.prepareContext(tomcat.getHost(), initializers);
        //启动tomcat并返回TomcatWebServer对象
        return this.getTomcatWebServer(tomcat);
    }

从上面的源码中,在getTomcatWebServer这步才是完成 Tomcat,其他部分都是在配置 Tomcat。我们进去方法里面看看。

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

TomcatWebServer 对象是 Spring Boot 对 Tomcat 对象的封装,内部存了 Tomcat 实例的引用,这里执行的是TomcatWebServer 的构造方法:

  public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
        this.monitor = new Object();
        this.serviceConnectors = new HashMap();
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
        this.initialize();
    }

这里我们主要看一下 this.initialize() ,在这个方法里面调用了 tomcat.start() 启动 Tomcat。

    private void initialize() throws WebServerException {
        logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
        synchronized(this.monitor) {
            try {
                //给Engine命名
                this.addInstanceIdToEngineName();
                //获取 Host 中的 Context
                Context context = this.findContext();
                //绑定 Context 的生命周期监听器
                context.addLifecycleListener((event) -> {
                    if (context.equals(event.getSource()) && "start".equals(event.getType())) {
                        this.removeServiceConnectors();
                    }

                });
                //启动 Tomcat,触发初始化监听器
                this.tomcat.start();
                //启动过程中子线程的异常从主线程抛出
                this.rethrowDeferredStartupExceptions();

                try {
                //给当前 Context 绑定类加载器
                    ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
                } catch (NamingException var5) {
                }
//Tomcat 的所有线程都是守护线程,这里启动一个阻塞的非守护线程来确保 Tomcat 能及时停止
                this.startDaemonAwaitThread();
            } catch (Exception var6) {
                this.stopSilently();
                this.destroySilently();
                throw new WebServerException("Unable to start embedded Tomcat", var6);
            }

        }
    }

好了,关于 Tomcat 的解析就到这里为止,希望对小伙伴有一点点的帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值