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.