SpringBoot内置Tomcat启动流程

概述

  上次一朋友面试问到了SpringBoot中内置Tomcat的启动流程,在此我也记录下,加深记忆。
  在SpringBoot中引入spring-boot-starter-web依赖,这个依赖里面又引入了spring-boot-starter-tomcat依赖,这样我们就可以直接使用Tomcat服务器,都不用配置。对于嵌入式Tomcat,其实也比较简单,就是调用Tomcat提供的外部类 org.apache.catalina.startup.Tomcat 就可以实现。

启动流程

SpringBoot内置的Tomcat启动要从SpringApplication类的run()方法说起,一路点下去,来到run()方法中,run方法返回的是一个ConfigurableApplicationContext 对象,此对象就是JavaWeb的ServletContext对象。

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment); // banner图案
            context = this.createApplicationContext(); // 创建context对象
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
			// 略部分代码
        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

在run()方法中又一个名为refreshContext()方法,点进去一看

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

        this.refresh(context);
}

再该方法中又调用了refresh()方法,点进去一看

protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext)applicationContext).refresh();
}

再点击refresh()方法,来到AbstractApplicationContext()抽象类中的非抽象方法refresh(),这个方法中可以说是定义了Tomcat的启动流程。其中较为关键的就是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();
                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();
            }

        }
    }

再单击 onRefresh()方法,发现没有写任何代码,那么肯定就是留给子类去重写,

protected void onRefresh() throws BeansException {
}

可以看到有五个类继承了AbstractApplicationContext,我们需要找的是ServletWebServerApplicationContext类,找到里面重写的 onRefresh()方法
在这里插入图片描述

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

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

可以看到,在ServletWebServerApplicationContext类的onRefresh()方法中调用了 createWebServer()方法,点进去一看。

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

可以看到,这里先获取到 ServletWebServerr工厂,然后再根据这个工厂获取具体的webServer。
getWebServer是接口中的方法,这个ServletWebServerFactory接口有4个实现类,这里获取到的工厂是TomcatServletWebServerFacotry这个工厂。
在这里插入图片描述
然后点进getWebServer()方法一看,这个方法是创建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);
        return this.getTomcatWebServer(tomcat);
    }

最后执行refresh()中调用的finishRefresh()方法

protected void finishRefresh() {
        this.clearResourceCaches();
        this.initLifecycleProcessor();
        this.getLifecycleProcessor().onRefresh();
        this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
        LiveBeansView.registerApplicationContext(this);
    }

finishRefresh()方法也在子类中重写了
在这里插入图片描述

protected void finishRefresh() {
        super.finishRefresh();
        WebServer webServer = this.startWebServer();
        if (webServer != null) {
            this.publishEvent(new ServletWebServerInitializedEvent(webServer, this));
        }
    }

点进 startWebServer()方法一看

private WebServer startWebServer() {
        WebServer webServer = this.webServer;
        if (webServer != null) {
            webServer.start();
        }

        return webServer;
    }

再点start()方法进去一看,此方法是WebServer接口中的方法,实现类如下
在这里插入图片描述
最后在调用TomcatWebServer中的start()方法启动Tomcat。

public void start() throws WebServerException {
        synchronized(this.monitor) {
            if (!this.started) {
                boolean var10 = false;

                try {
                    var10 = true;
                    this.addPreviouslyRemovedConnectors();
                    Connector var2 = this.tomcat.getConnector();
                    if (var2 != null && this.autoStart) {
                        this.performDeferredLoadOnStartup();
                    }

                    this.checkThatConnectorsHaveStarted();
                    this.started = true;
                    logger.info("Tomcat started on port(s): " + this.getPortsDescription(true) + " with context path '" + this.getContextPath() + "'");
                    var10 = false;
                } catch (ConnectorStartFailedException var11) {
                    this.stopSilently();
                    throw var11;
                } catch (Exception var12) {
                    PortInUseException.throwIfPortBindingException(var12, () -> {
                        return this.tomcat.getConnector().getPort();
                    });
                    throw new WebServerException("Unable to start embedded Tomcat server", var12);
                } finally {
                    if (var10) {
                        Context context = this.findContext();
                        ContextBindings.unbindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
                    }
                }

                Context context = this.findContext();
                ContextBindings.unbindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
            }
        }
    }

文字总结

  SpringBoot内置Tomcat启动流程要从main函数入手,而main函数中的run()方法实际上是调用SpringApplication的run()方法。在run()方法中,先创建一个ConfigurableApplicationContext对象,通过createApplicationContext()对象进行创建,这个对象实际上就是JavaWeb的ApplicationContext对象。然后调用refreshContext()方法,在该方法中,又调用了refresh()方法,此方法中定义了Tomcat创建流程,调用ServletWebServerApplicationContext的onRefresh()方法,在该方法中调用了createWebServer()方法,在该方法中,先获取ServletWebServerFactory,再根据工厂获取具体的webServer,此时获取的是TomcatServletWebServerFacotry这个工厂,然后在getWebServer()方法中,创建Tomcat的一些核心组件。然后调用getTomcatWebServer()方法,进行初始化Tomcat。最后调用refresh()中的finishRefresh()方法,该方法被ServletWebServerApplicationContext子类重写,在该方法中调用start()方法将Tomcat启动。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值