Spring boot embedded Tomcat principle
Embedded Tomcat startup principle
- First, come to the place where the SpringBoot project is started, that is, the configuration class
@SpringBootApplication public class InakiApplication { public static void main(String[] args) { SpringApplication.run(InakiApplication.class, args); } }
- Click the @ SpringBootApplication annotation to enter
3. In the figure above, the * * @ Import annotation is used to introduce the AutoConfigurationImportSelector * * class. What does this class do? When you enter the source code, you first call the **selectImport() * method. In this method, you call the **getAutoConfigurationEntry () method, then you call the getCandidateConfigurations() * method, and the getCandidateConfigurations() method goes to META-INF/spring.. Load relevant configuration classes in the factories configuration file.
META-INF/spring. Factories: SPI mechanism of SpringBoot. When SpringBoot starts, it will scan all files with this name, get all classes that need to be automatically assembled, and scan them.
META-INF/spring.factories in spring boot autoconfiguration: XXX In the release package
Continue to view the org.org of the modified configuration class springframework. boot. autoconfigure. Value of enableautoconfiguration
The automatic configuration class of tomcat server is the servletwebserverfactory autoconfiguration. Click to enter this class.
4. First look at the notes of this class
Auto-configuration for servlet web servers.
Automatic configuration of servlet web server
-
Continue to look at the annotation of this class
In this class, embedded container classes such as EmbeddedTomcat, EmbeddedJetty and embedded undertow are also loaded through the @ Import annotation. springboot starts the embedded tomcat container by default. If you want to change the startup jetty or underow container -
Take a look at the ServerProperties class in the EnableConfigurationProperties annotation
// Go to the configuration file to find the configuration information starting with server @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { /** * Set server port number */ private Integer port; /** * Set web server address */ private InetAddress address;
This proves why we need to be in application properties/application. Configure server in YML file port,server.address will take effect.
- We click to enter the EmbeddedTomcat class
Mainly focus on the method of tomcatServletWebServerFactory
@Configuration(proxyBeanMethods = false) class ServletWebServerFactoryConfiguration { // The class must meet the conditions declared in the @ ConditionalOnClass and @ ConditionalOnMissingBean annotations to take effect // @ Con@ConditionalOnMissingBeanditionalOnClass , since we imported the dependency package of tomcat, these three classes must exist in all contexts // @ConditionalOnMissingBean, the ServletWebServerFactory class does not exist in the context @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedTomcat { @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) { // 1. Create Tomcat webserver factory TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); // The following methods are used to set the property value of tomcatwebServeFactory factory.getTomcatConnectorCustomizers() .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatContextCustomizers() .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatProtocolHandlerCustomizers() .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } /** * Because the package of jetty is not imported, the conditions in the second annotation cannot be met, so this class will not be loaded and will not be automatically configured */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedJetty { @Bean public JettyServletWebServerFactory JettyServletWebServerFactory( ObjectProvider<JettyServerCustomizer> serverCustomizers) { JettyServletWebServerFactory factory = new JettyServletWebServerFactory(); factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } /** * Similarly, embeddedjetty class */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedUndertow { @Bean public UndertowServletWebServerFactory undertowServletWebServerFactory( ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers, ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) { UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); factory.getDeploymentInfoCustomizers() .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList())); factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } }
The code in the text has been annotated.
- In this class, only the embbedtomcat condition can be satisfied. Now go to TomcatServletWebServerFactory
The following is a key method getWebServer. It is the class that creates Tomcat and completes the startup.
public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } // Create tomcat Tomcat tomcat = new Tomcat(); // Set baseDir File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); // Gets the Connector for receiving and processing requests Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); // Add Connector to webServer server tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); // Turn off automatic deployment tomcat.getHost().setAutoDeploy(false); // Set Engine configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }
Finally, return getTomcatWebServer(tomcat); Continue to getTomcatWebServer()
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, getPort() >= 0); } public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; // The startup method is here initialize(); }
Here is how SpringBoot automatically configures and starts the Tomcat container.
Where is Tomcat configured during SpringBoot startup
As mentioned above, the core method of tomcat startup is in the TomcatServletWebServerFactory#getWebServer() method,
Let's take a look at where getWebServer is called.
Call process of getWebServer
Click the run method to enter the SpringApplication#run(String... args) method.
public ConfigurableApplicationContext run(String... args) { //StopWatch is mainly used to count the execution time of each task, such as the total time taken by Spring Boot startup. StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //Step 1: get and start the listener by loading meta-inf / spring Factories completed SpringApplicationRunListener Instantiation work SpringApplicationRunListeners listeners = getRunListeners(args); //In fact, it calls the starting() method of the EventPublishingRunListener class listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //Step 2: construct container environment. In short, load system variables, environment variables and configuration files ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //Set the bean to be ignored configureIgnoreBeanInfo(environment); //Print banner Banner printedBanner = printBanner(environment); //Step 3: create container context = createApplicationContext(); //Step 4: instantiate springbootexceptionreporter Class, used to support the report on startup error exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //Step 5: prepare container this step is mainly the preparation action before container refresh. Contains a key operation: Inject the startup class into the container to lay the foundation for subsequent automatic configuration. prepareContext(context, environment, listeners, applicationArguments, printedBanner); //Step 6: the processing work related to refreshing the container springBoot has been completed, and the next work will be handed over to you spring. Internal call spring of refresh method, // refresh method plays an important role in the whole source code system of spring and is the key to realize ioc and aop. refreshContext(context); //Step 7: refresh the template method in the extension interface design pattern after the container. It is empty by default. If there is customization, you need to The method can be overridden. For example, print some startup ends logļ¼Or some other post-processing. afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //Publish events that the app has started listeners.started(context); /* This method probably does the following things 1. Get and start the listener by loading meta-inf / spring Factories completed SpringApplicationRunListener Instantiation work 2. Constructing container environment, in short, is to load system variables, environment variables and configuration files 3. Create container 4. Instantiate springbootexceptionreporter Class, used to support reporting errors about startup 5. Prepare container 6. Refresh container 7. Extension interface after refreshing container Then the built-in tomcat startup source code is hidden in step 6 of the appeal: the refreshContext method, which will eventually be called Use the refresh() method of AbstractApplicationContext class Enter the refreshContext() method, as shown in the figure: * Traverse all registered ApplicationRunner and CommandLineRunner, and execute their run() side Law. * We can implement our own ApplicationRunner or CommandLineRunner to SpringBoot Expand the startup process of. */ callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //Listening events that the application has started and completed listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
The main process of this method is the following seven methods, and the serial number has been marked in the above code.
- Get and start the listener by loading meta-inf / spring Factories completed the instantiation of spring applicationrunlistener
- Constructing container environment, in short, is to load system variables, environment variables and configuration files
- Create container
- Instantiate springbootexceptionreporter Class, used to support reporting errors about startup
- Access container
- Refresh container refresh
- Refresh container's extension interface
The process of building in tomcat is in the refresh() method in step 6
Click enter to go to AbstractApplicationContext#refresh() The code has been streamlined
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare context prepareRefresh(); // Get beanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Set some properties and fill in beanFactory prepareBeanFactory(beanFactory); try { // Load the post processor of the assembly beanFactory postProcessBeanFactory(beanFactory); // Post processor to execute beanFactory // The acquisition of automatic configuration class is here. At this time, all beans are saved in BeanDefinition. invokeBeanFactoryPostProcessors(beanFactory); // Register beanpossessor bean level postprocessor registerBeanPostProcessors(beanFactory); // Dealing with internationalization related content initMessageSource(); // Initialize applicationeventmulticast initApplicationEventMulticaster(); // Load the web server, and the initialization of tomcat is here onRefresh(); // Register listener registerListeners(); // Load all bean s with delayed initialization finishBeanFactoryInitialization(beanFactory); // Complete the startup of the web server finishRefresh(); } } }
The assembly process of tomcat is in onRefresh() method. Click to enter.
After entering, it is found that this is a template method, and subclasses are needed to rewrite the implementation.
Click to enter servletwebserver ApplicationContext
protected void onRefresh() { // Call parent method super.onRefresh(); try { // Core, create Tomcat webserver createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
createWebServer() starts the web service, but Tomcat has not been started yet
Breakpoint entry:
Both webServer and servletContext are empty. Enter 179 lines of code and get the last line of servletwebserver applicationcontext#getwebserverfactory() from the factory
What it wants to get is a factory of type servletwebserverfactory. It seems to me to analyze the servletwebserverfactory class. It is an interface, that is, there is a method of getWebServer. Does this method look familiar. Yes, it is the getWebServer method in TomcatServletWebServerFactory, which is the core configuration process of tomcat.
This factory has the XXXWebServerFactory mentioned when we talked about Tomcat automatic configuration just now. You can see Tomcat and Jetty here.
Release breakpoint:
At this point, you get TomcatServletWebServerFactory and call its getWebServer method.
At this point, the configuration process of tomcat is finished.
Summary
springboot starts a built-in Tomcat by means of new Tomcat(). But there is another problem here. Tomcat is only started here, but how does our spring MVC load? In the next chapter, we will talk about how spring boot automatically assembles spring MVC
flow chart: