Principle analysis of Tomcat embedded in Spring Boot

preface

Why doesn't Spring Boot need to install additional Tomcat?

Because Spring Boot has a built-in Web server Tomcat, it does not need to be configured separately.

This article mainly analyzes the implementation principle of Tomcat embedded in Spring Boot from the perspective of source code, and discusses when Tomcat is created, started and how to start.

Follow the source code step by step to find out how to start the built-in tomcat.

First, post the startup class:

@SpringBootApplication
public class QuartJobApplication {

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

}

We click run to enter the source code:

    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 {
             .......ellipsis.......
            this.refreshContext(context);
            ........ellipsis.......

    }

The source code omitted above has been mentioned in the last article I shared. It will not be repeated here. Interested partners can take a look:

Principle analysis of Spring Boot startup process

Our main concern here is this refreshContext(context); This method, because Tomcat is here.

Now let's click in and have a look


We then click the refresh method


Select the class of servletwebserver ApplicationContext here

After entering the following method, select onRefresh

    public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            //1. Call the method that the container is ready to refresh, obtain the current time of the container, and set the synchronization ID for the container
            this.prepareRefresh();
            //2. Creating beanFactory is mainly to load bean information, which is divided into positioning, loading and registration
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //3. Configure container features for BeanFactory, such as class loader, event handler, and so on
            this.prepareBeanFactory(beanFactory);

            try {
                 //4. Post processing after BeanFactory preparation;
                 //Abstract method, which is not processed at present. Subclasses override this method to make further settings after the BeanFactory is created and prepared
                this.postProcessBeanFactory(beanFactory);
                //5. Call all registered beanfactoryprocessor beans
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //6. Register the BeanPost event handler for BeanFactory This is just registration, when calling the getbean
                this.registerBeanPostProcessors(beanFactory);
                //7. Initialize MessageSource component (internationalization function; message binding, message parsing)
                this.initMessageSource();
                //8. Initialize container event propagator
                this.initApplicationEventMulticaster();
                //9. Call some special Bean initialization methods of subclasses (here is what we mainly look at)
                this.onRefresh();
                //10. Register event listeners for event propagators
                this.registerListeners();
                //11. Initialize all remaining singleton beans
                this.finishBeanFactoryInitialization(beanFactory);
                //12. After completing the initialization and creation of BeanFactory, the IOC container is created
                this.finishRefresh();
            } catch (BeansException var10) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                }
                //13. Destroy the created Bean
                this.destroyBeans();
                //14. Cancel the refresh operation and reset the synchronization ID of the container
                this.cancelRefresh(var10);
                throw var10;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

The general meaning of each method above is like this. Let's select onRefresh method to see the source code:

Here, select the method in the servlet webserver ApplicationContext class

Click this The method of createwebserver. The source code is as follows:

    private void createWebServer() {
       //The first time I came here, this is null
        WebServer webServer = this.webServer;
        //It is also null here
        ServletContext servletContext = this.getServletContext();
        //For the first time, the above webServer servletContext is null and will enter the if branch
        if (webServer == null && servletContext == null) {
         //There is no need to pay attention here, just mark it
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            //Get Servlet server factory
            ServletWebServerFactory factory = this.getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
            //Factory method, get the Servlet server and set it as a property of 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);
            }
        }
       //Initialize some ServletContext information in the ConfigurableEnvironment
        this.initPropertySources();
    }

We choose factory Click getwebserver to enter the source code:

The getWebServer method mainly deals with the creation, environment configuration and startup of Tomcat container objects.

    public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        //Instantiating Tomcat can be understood as a Server component
        Tomcat tomcat = new Tomcat();
        //Create a temporary Tomcat file path
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        //To create a connection protocol, http1.0 is used by default 1 protocol, NIO network model
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        //Create the host and turn off the hot deployment
        tomcat.getHost().setAutoDeploy(false);
       // Configuration engine
        this.configureEngine(tomcat.getEngine());
        
        Iterator var5 = this.additionalTomcatConnectors.iterator();

        while(var5.hasNext()) {
            Connector additionalConnector = (Connector)var5.next();
            tomcat.getService().addConnector(additionalConnector);
        }
        //Initialize TomcatEmbeddedContext
        this.prepareContext(tomcat.getHost(), initializers);
        //Start tomcat and return the tomcat webserver object
        return this.getTomcatWebServer(tomcat);
    }

From the above source code, Tomcat is completed in the step of gettomcat webserver, and other parts are configuring Tomcat. Let's go inside.

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

TomcatWebServer object is the encapsulation of Tomcat object by Spring Boot. The reference of Tomcat instance is stored inside. Here is the construction method of 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();
    }

Here we mainly look at this Initialize(), in which Tomcat. Com is called Start() starts Tomcat.

    private void initialize() throws WebServerException {
        logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
        synchronized(this.monitor) {
            try {
                //Name the Engine
                this.addInstanceIdToEngineName();
                //Get Context in Host
                Context context = this.findContext();
                //Bind the Context's lifecycle listener
                context.addLifecycleListener((event) -> {
                    if (context.equals(event.getSource()) && "start".equals(event.getType())) {
                        this.removeServiceConnectors();
                    }

                });
                //Start Tomcat and trigger the initialization listener
                this.tomcat.start();
                //The exception of the sub thread in the startup process is thrown from the main thread
                this.rethrowDeferredStartupExceptions();

                try {
                //Bind classloader to current Context
                    ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
                } catch (NamingException var5) {
                }
//All threads of Tomcat are daemon threads. Here, start a blocked non daemon thread to ensure that Tomcat can stop in time
                this.startDaemonAwaitThread();
            } catch (Exception var6) {
                this.stopSilently();
                this.destroySilently();
                throw new WebServerException("Unable to start embedded Tomcat", var6);
            }

        }
    }

Well, that's all for the analysis of Tomcat. I hope it can be a little help to my little partner.

Keywords: Java Spring Boot Tomcat

Added by TashaAT on Mon, 10 Jan 2022 03:10:26 +0200