Tomcat在SpringBoot中的启动

tomcat作为springboot嵌入服务器时,是单独为当前springboot提供服务的,所以有很多地方可以简化:

  • 支持项目
    独立服务器:多项目、多Servlet
    嵌入式:一般单项目(嵌入的项目)、单Servlet(DispatchServlet)
  • server构建
    独立服务器:Catalina通过解析server.xml构建
    嵌入式:源码构建
  • 启动方式
    独立服务器:Bootstrap引导启动
    嵌入式:Tomcat类启动

对比了两种启动方式的差别,下面重点来看下springboot中是如何简化创建server并注入DispatchServlet的。
首先看springboot的启动入口:
SpringApplication.run:

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

主要是创建spring容器ApplicationContext,并进行刷新,在servlet模式下实现类为AnnotationConfigServletWebServerApplicationContext,复写了onRefresh方法,在容器刷新时,进行webServer的创建工作。

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }
    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();
    }

默认配置下,ServletWebServerFactory实现为TomcatServletWebServerFactory,采用工厂模式来创建Tomcat服务器:

@Override
    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);
    }

这里我们可以发现,和独立服务器不一样,并没有创建Bootstrap、Catalina类,也没有通过server.xml配置文件来创建server,而是直接new了一个Tomcat,配置上connector、Engine、Host、Context等组件,在创建TomcatWebServer时启动tomcat:

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        initialize();
    }
private void initialize() throws WebServerException {
        logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
        ...省略其他代码
        this.tomcat.start();
        ...省略其他代码

创建完成的服务器包含一个service,一个connector, 一个engine,一个host和一个context,tomcat.start时调用了server.start(),剩下的就和独立部署时一样,启动这些connector,container了。
tomcat启动的流程就结束了,但是还有一个问题,这个过程中并没有看到tomcat加载Servlet,也没有解析web.xml,spring是通过DispatchServlet来处理请求的,它是怎么加载到容器中的呢?
回到spring创建webServer的地方:

ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());

可以看到它传入了一个ServletContextInitializer,这个initializer经过一系列封装最终传入了前面创建的唯一的Context里面,在Context启动的初始化阶段会进行回调,DispatchServlet的添加就在这个initializer里面,initializer的实现为:

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

它的功能是从spring容器中找出所有实现了ServletContextInitializer来进行调用,而springboot的自动装配机制注入了DispatcherServletRegistrationBean,它实现了ServletContextInitializer,在tomcat容器启动时向context中注册了DispatcherServlet,具体流程代码就不贴了。
Tomcat启动完成注入了DispatcherServlet后,DispatcherServlet即可处理请求了。

你可能感兴趣的:(Tomcat在SpringBoot中的启动)