SpringBoot source code analysis-Tomcat, SpringMVC start

Posted May 27, 20205 min read

This article shares the startup process of Tomcat and SpringMvc components in SpringBoot by reading the SpringBoot source code.
Source code analysis is based on spring boot 2.1

Tomcat

In the analysis [SpringBoot startup]( https://mp.weixin.qq.com/s?__biz=MzI2MDQzMTU2MA==&mid=2247483776&idx=1&sn=4190ad31460fd0335fbd926a253e78f0&chksm=ea688a27dd1f0331d2ba8704b973daecdc3 The ApplicationContext used is AnnotationConfigServletWebServerApplicationContext.
Its parent class, ServletWebServerApplicationContext, creates and initializes WebServer through ServletWebServerFactory. WebServer is compatible with different servlet containers(tomcat, jetty, netty) and provides unified start and stop operations.
ServletWebServerApplicationContext is also responsible for registering Servlet, Filter, and ServletContextListener.
ServletWebServerApplicationContext # servletConfig and GenericWebApplicationContext # servletContext are all classes provided by the servlet specification.

ApplicationContext # run-> AnnotationConfigServletWebServerApplicationContext # refresh-> AbstractApplicationContext # refresh-> AbstractApplicationContext # onRefresh-> ServletWebServerApplicationContext # onRefresh-> ServletWebServerApplicationContext # createWebServer

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

# 1 Create webServer, note that getSelfInitializer() constructs a ServletContextInitializer through method reference, the ServletContextInitializer will call the ServletWebServerApplicationContext # selfInitialize method
# 2 webServer already exists, call ServletWebServerApplicationContext # selfInitialize directly

In ServletWebServerFactoryConfiguration, determine the servlet container dependency in the current JAVA environment, and construct the corresponding WebServerFactory.
Here only focus on tomcat.
TomcatServletWebServerFactory # getWebServer

public WebServer getWebServer(ServletContextInitializer ... initializers) {
    //#1
    Tomcat tomcat = new Tomcat();
    File baseDir =(this.baseDirectory! = Null)? This.baseDirectory:createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    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);
    }
    //#2
    prepareContext(tomcat.getHost(), initializers);
    //# 3
    return getTomcatWebServer(tomcat);
}

# 1 Construct Tomcat object and configure related properties
# 2 Pre-configure Tomcat Context
# 3 into Spring's TomcatWebServer object

The # 2 step will construct a TomcatStarter, which is a ServletContainerInitializer implementation class. ServletContainerInitializer is also a class provided by the servlet specification. After the servlet container is started, ServletContainerInitializer # onStartup is called.

TomcatStarter # initializers is the ServletContextInitializer array. TomcatStarter # onStartup will traverse the array and call ServletContextInitializer # onStartup.
The ServletWebServerApplicationContext # createWebServer method # 1 step calls the getSelfInitializer() method to construct a ServletContextInitializer, which is always passed to TomcatServletWebServerFactory # getWebServer and is used to construct TomcatStarter.
So tomcat will eventually call ServletWebServerApplicationContext # selfInitialize

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

# 1 Get relevant properties from ServletContext and set them in SpringContext.
# 2 Find the ServletContextInitializer in the Spring context and call the onStartup method.
ServletContextInitializer is a configuration interface that configures Servlet 3.0+ ServletContext programmatically.
Its implementation class FilterRegistrationBean, ServletRegistrationBean, ServletListenerRegistrationBean can inject Filter, Servlet, ServletContextListener into ServletContext.

TomcatWebServer # Constructor-> TomcatWebServer # initialize

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

            //# 3
            this.tomcat.start();

            rethrowDeferredStartupExceptions();

            //# 4
            try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass(). GetClassLoader());
            }
            catch(NamingException ex) {
                //Naming is not enabled. Continue
            }

            //# 5
            startDaemonAwaitThread();
        }
        catch(Exception ex) {
            stopSilently();
            destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", ex);
        }
    }
}

# 1 Print Tomcat startup log
# 2 Get Tomcat Context and configure related properties
# 3 Start Tomcat and call ServletContainerInitializer # onStartup
# 4 Bind to ClassLoader
# 5 Because all tomcat threads are background threads, it is necessary to create a blocking non-background thread to keep the process.

SpringMVC

SpringMVC component startup is relatively simple.

WebMvcAutoConfiguration is responsible for the construction of RequestMappingHandlerAdapter, HandlerMethodReturnValueHandler, HttpMessageConverter, RequestMappingHandlerMapping and other components
DispatcherServletAutoConfiguration is responsible for constructing DispatcherServlet components
About SpringMVC, the article before you can reference - Spring Mvc principle

DispatcherServletRegistrationBean is used to register DispatcherServlet to ServletContext in DispatcherServletAutoConfiguration. DispatcherServletRegistrationBean inherits ServletRegistrationBean, and finally the injection operation is completed by ServletWebServerApplicationContext # selfInitialize.

@WebFilter, @WebListener, @WebServlet can also be injected into Servlet, Filter, ServletContextListener.(The three interfaces are provided by the servlet specification)
These annotations are processed by the @ServletComponentScan annotation. The @ServletComponentScan annotation is introduced into the ServletComponentScanRegistrar through @Import. The ServletComponentScanRegistrar implements the ImportBeanDefinitionRegistrar(about the ImportBeanDefinitionRegistrar interface, you can review the previous analysis [SpringBoot AutoConfigure]( https://mp.weixin.qq.com/s?__biz = MzI2MDQzMTU2MA == & mid = 2247483783 & idx = 1 & sn = 4fade548fb679911517fea3f30fc2f6b & chksm = ea688a20dd1f0336658f8b44df5c58e6c910029283d2003562293dd809691ca2e960ee08d0601 #
The PostProcessor scans the @WebFilter, @WebListener, @WebServlet annotations in the corresponding directory and uses the corresponding ServletComponentHandler for processing. Interested students can read the code by themselves.

If you think this article is good, welcome to pay attention to my WeChat public account, your attention is my motivation!