-
Introduction [1]
-
Start with the Main method [2]
-
Inside Tomcat [3]
-
Conclusion [4]
preface
We know that SpringBoot brings us a new development experience, we can directly start the Web program into jar package, this is due to SpringBoot built-in container, you can directly start, this article will take Tomcat as an example. Take a look at how SpringBoot starts Tomcat, and learn the source code and design of Tomcat.
Start with the Main method
As anyone who has used SpringBoot knows, you first write a main method to start
@SpringBootApplicationpublicclass TomcatdebugApplication { public static void main(String[] args) { SpringApplication.run(TomcatdebugApplication.class, args); }}
Copy the code
We just click run method source code, tracking down, send the final run method is invoked ConfigurableApplicationContext method, the source code is as follows:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; CollectionexceptionReporters = new ArrayList<>(); / / set the system property "Java. The awt. Headless", to true will enable headless mode support configureHeadlessProperty (); / / by * * SpringFactoriesLoader * retrieval meta-inf/spring. Factories *, / / find the statement of all SpringApplicationRunListener implementation class to instantiate, / / after one by one to call its started () method, radio SpringBoot to start execution SpringApplicationRunListeners listeners = getRunListeners (args); // Publish the app to start the event listeners. Starting (); Try {/ / initialization parameter ApplicationArguments ApplicationArguments = new DefaultApplicationArguments (args); // Create and configure the Environment that the current SpringBoot application will use (including PropertySource and Profile to use), / / and traverse call all SpringApplicationRunListener environmentPrepared () method, radio Environment ready. ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); PrintedBanner = printBanner(environment); // createApplicationContext context = createApplicationContext(); // Retrieve * meta-inf /spring.factories* with *SpringFactoriesLoader*, Obtain and instantiate anomalies analyzer exceptionReporters = getSpringFactoriesInstances (SpringBootExceptionReporter. Class, new Class[] { ConfigurableApplicationContext.class }, context); / / as the ApplicationContext loading environment, after each perform ApplicationContextInitializer the initialize () method to further encapsulate ApplicationContext, / / and call all SpringApplicationRunListener contextPrepared () method, the EventPublishingRunListener provides only an empty contextPrepared () method). / / after initialization of the IoC container, and call SpringApplicationRunListener contextLoaded () method, radio ApplicationContext IoC loading is completed, // This includes the autoconfiguration classes imported via ** @enableAutoConfiguration **. prepareContext(context, environment, listeners, applicationArguments, printedBanner); // refreshContext(context); // Refresh the context again, which is actually an empty method, possibly for future extensions. afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // Publish the events that the app has started, listeners. Started (context); // Iterates over all registered ApplicationRunners and CommandLinerunners and executes their run() method. // We can extend the SpringBoot boot process by implementing our own ApplicationRunner or CommandLineRunner. 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; }Copy the code
In fact, this method can be summarized in the following steps: > 1. Configure Properties > 2. Get the listener, publish the application start event > 3. Initialize the input parameters > 4. Configure environment, output banner > 5. Create context > 6. Preprocessing context > 7. Refresh context > 8. Refresh context > 9. Publish the event that the application is started > 10. Publish the event that the application is started
When analyzing the Tomcat content, you only need to focus on two things: how the context is created and how the context is refreshed. The corresponding methods are createApplicationContext() and refreshContext(context), and let’s see what these methods do.
protected ConfigurableApplicationContext createApplicationContext() { Class contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { thrownew IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }Copy the code
Here is to determine which type of Servlet to create according to our webApplicationType. The code corresponds to the Web type (Servlet), REACTIVE Web type (REACTIVE Web type), and non-Web type (default) Web type, so must instantiate DEFAULT_SERVLET_WEB_CONTEXT_CLASS specified class, namely AnnotationConfigServletWebServerApplicationContext class
Let’s use a diagram to illustrate the relationship of this class
We can know through the class diagram, the class inheritance is ServletWebServerApplicationContext, this is our real leading role, and this class is inherited AbstractApplicationContext finally, after understanding the create a context of situation, Let’s look at the refresh context again. The relevant code is as follows:
/ / class: SpringApplication. Javaprivate void refreshContext (ConfigurableApplicationContext context) {/ / direct call the refresh method refresh (context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) {// Not allowed in some environments.}}}// class: SpringApplication.javaprotected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }Copy the code
Here or directly call the refresh (context) method, the last is equivalent to the parent class AbstractApplicationContext call the refresh () method, the code is as follows:
/ / class: AbstractApplicationContextpublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. What that means is that each subclass calls onRefresh() onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); }}}Copy the code
Here we see onRefresh () method is called the subclass implementation, according to our analysis of the above, we here is a subclass of ServletWebServerApplicationContext.
/ / class: ServletWebServerApplicationContextprotected void onRefresh () {super. OnRefresh (); try { createWebServer(); } catch (Throwable ex) { thrownew 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()); } elseif (servletContext ! = null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { thrownew ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }Copy the code
CreateWebServer () : createWebServer() : createWebServer(); Let’s take a look at the factory.
Inside Tomcat
As we can see from the figure above, the factory class is an interface, and the implementation of each specific service is implemented by each subclass
So we went to see TomcatServletWebServerFactory. GetWebServer () implementation.
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { 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); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }Copy the code
Based on the code above, we can see that it does two main things. The first thing is to add the Connnctor(we call it the connector) object to Tomcat, and the second thing is the configureEngine, which we can barely understand. So what is this Engine?
Let’s look at the source of tomcat.getengine () :
public Engine getEngine() { Service service = getServer().findServices()[0]; if (service.getContainer() ! = null) { return service.getContainer(); } Engine engine = new StandardEngine(); engine.setName( "Tomcat" ); engine.setDefaultHost(hostname); engine.setRealm(createDefaultRealm()); service.setContainer(engine); return engine; }Copy the code
According to the above source code, we found that the Engine is a Container, we continue to trace the source code, find the Container interface
Here, we see the four subsystems interface, respectively is the Engine, the Host, the Context, the Wrapper. We know from inheritance that they are both containers
So what’s the difference? Let me see what their notes say.
/** If used, an Engine is always the top level Container in a Catalina * hierarchy. Therefore, the implementation's setParent() method * should throw IllegalArgumentException. * * @author Craig R. McClanahan */ PublicInterface Engine extends Container {// omit code}/** ** The parent Container attached to a Host is generally an Engine, but may * be some other implementation, or may be omitted if it is not necessary. * * The child containers attached to a Host are generally implementations * of Context (representing an individual servlet context). * * @author Craig R. McClanahan */public interface Host extends Container {// omit code}/*** * The parent Container attached to a Context is generally a Host, but may * be some other implementation, or may be omitted if it is not necessary. * * The child containers attached to a Context are generally implementations * of Wrapper (representing individual servlet definitions). * * * @author Craig R. McClanahan */public interface Context extends Container, ContextBind {// ContextBind}/** * The parent Container attached to a Wrapper will generally be an * implementation of Context, representing the servlet context (and * therefore the web application) within which this servlet executes. * * Child Containers are not allowed on Wrapper implementations, so the * addChild() method should throw an * IllegalArgumentException. * * @author Craig R. McClanahan */publicinterface Engine extends Container {// omit Container} Engine extends Container {// omit Container} Engine extends Container {// omit Container} Engine extends Container {// omit Container} Engine extends Container {// omit Container} Engine extends Container {// omit Container} Engine extends Container {// omit Container} Engine extends Container Engine>Host>Context>Wrapper Let's look at the source code for the Tomcat class again :// part of the source code, the rest omitted. Public void setConnector(Connector Connector) {Service Service = getService(); boolean found = false; for (Connector serviceConnector : service.findConnectors()) { if (connector == serviceConnector) { found = true; } } if (! found) { service.addConnector(connector); Public service getService() {return getServer().findServices()[0]; } public void setHost(Host Host) {Engine Engine = getEngine(); boolean found = false; for (Container engineHost : engine.findChildren()) { if (engineHost == host) { found = true; } } if (! found) { engine.addChild(host); }} public Engine getEngine() {Service Service = getServer().findServices()[0]; if (service.getContainer() ! = null) { return service.getContainer(); } Engine engine = new StandardEngine(); engine.setName( "Tomcat" ); engine.setDefaultHost(hostname); engine.setRealm(createDefaultRealm()); service.setContainer(engine); return engine; } public server getServer() {if (server! = null) { return server; } System.setProperty("catalina.useNaming", "false"); server = new StandardServer(); initBaseDir(); // Set configuration source ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null)); server.setPort( -1 ); Service service = new StandardService(); service.setName("Tomcat"); server.addService(service); return server; Public Context addContext(Host Host, String contextPath, String contextName, String contextName, String contextName) String dir) { silence(host, contextName); Context ctx = createContext(host, contextPath); ctx.setName(contextName); ctx.setPath(contextPath); ctx.setDocBase(dir); ctx.addLifecycleListener(new FixContextListener()); if (host == null) { getHost().addChild(ctx); } else { host.addChild(ctx); Public static Wrapper addServlet(Context CTX, String servletName, Servlet servlet) { // will do class for name and set init params Wrapper sw = new ExistingStandardWrapper(servlet); sw.setName(servletName); ctx.addChild(sw); return sw; }} Tomcat getServer();}} Tomcat getServer(); GetEngine () allows us to know that there are multiple services in the Server, and that a Service represents an application that we deploy. We can also know that the Engine container, There is only one service; If we look at the setHost() source code, we can see that there are multiple host containers. Similarly, if we look at the addContext() source code, we can see that there are multiple contexts; AddServlet () indicates that there are multiple Wrapper containers, and this code implies that the Wrapper and Servlet are the same. In addition to setConnector source code, we can know that the Connector (Connector) is set under the service, and is possible to set multiple connectors (Connector). Based on the above analysis, we can summarize: Tomcat mainly contains two core components, Connector and Container, which are shown as follows: A Tomcat is a Server, and a Server has multiple services, which means that we deploy multiple applications. An application has multiple connectors and a Container, and a Container has multiple subcontainers. The relationship is shown as follows: Engine has multiple Host containers, Host has multiple Context containers, and Context has multiple Wrapper containers. SpringBoot is started by new SpringApplication() instance. The startup process mainly does the following things: 1. Get the listener and publish the application start event. 3. Initialize the input parameters. 5. Create context 6. Preprocess context 7. Refresh context 8. Refresh context again 9. Publish the event that the application has started 10. Publishing the application startup completion event and starting Tomcat is "refresh context" in step 7; Tomcat starts by initializing two core components, connectors and containers. A Tomcat instance is a Server, and a Server contains multiple services, that is, multiple applications. Each Service contains multiple connectors (Connetor) and a Container (Container), and the Container has multiple child containers, according to the parent relationship: Engine, the Host, the Context, Wrapper, which in addition to the Engine, the rest of the container is there can be multiple. The END the author: wood carpenter source: https://my.oschina.net/luozhou/blog/3088908 copyright owned by the author in this paperCopy the code