Byte Two: Tell me about your understanding of Spring boot's tomcat process source code loading web container!

Byte Two: Tell me about your understanding of Spring boot's tomcat process source code loading web container!

Preface

Spring boot load web container tomcat process source code analysis

My local springboot version is 2.5.1, and the subsequent analysis is based on this version

<parent> < groupId > org.springframework.boot </groupId > < artifactId > spring-boot-starter-parent </artifactId > < version > 2.5.1 </version > < relativePath/> <!-- lookup parent from repository --> </parent> Copy code

We introduce in the pom file

<dependency> < groupId > org.springframework.boot </groupId > < artifactId > spring-boot-starter-web </artifactId > </dependency> Copy code

To introduce the web container, the default web container is tomcat.

This article mainly describes the part of spring boot loading the web container tomcat. In order to avoid the knowledge points of the article being too scattered, other related such as bean loading, tomcat internal process, etc. will not be discussed in depth. Spring Boot practical study notes .

1. In the springboot web project, the global context is AnnotationConfigServletWebApplicationContext

In the following part, let s take a closer look

First of all, our entry code is generally written like this

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

Jump to the run method, and the following two methods will be called in turn

public static ConfigurableApplicationContext run ( Class<?> primarySource, String ... args ) { return run( new Class<?>[] {primarySource }, args); } Copy code
public static ConfigurableApplicationContext run ( Class<?>[] primarySources, String [] args ) { return new SpringApplication(primarySources).run(args); } Copy code

1. the SpringApplication instance object will be created, jump to the SpringApplication construction method to have a look, and the following methods will be called in turn

public SpringApplication ( Class<?>... primarySources ) { this ( null , primarySources); } Copy code
@SuppressWarnings({ "unchecked" , "rawtypes" }) public SpringApplication ( ResourceLoader resourceLoader, Class<?>... primarySources ) { ...... //this is not all the relevant code omitted, leaving only the relevant code //this.webApplicationType here = WebApplicationType.SERVLET, we have to analyze this specific code assigned to perform the this .webApplicationType = WebApplicationType.deduceFromClasspath (); ...... } Copy code

Continue to jump to WebApplicationType.deduceFromClasspath() to have a look

//This method is mainly to find whether the specified class exists in the current class path, and return the enumeration type static WebApplicationType deduceFromClasspath () { //WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet"; //We introduce spring-boot-starter-web through the pom file, and introduce spring-webmvc in brief. The above class is in this webmvc, so we will not enter this if branch if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null ) &&! ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null )) { return WebApplicationType.REACTIVE; } //SERVLET_INDICATOR_CLASSES = {"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" } //javax.servlet.Servlet This class exists in tomcat-embed-core //org.springframework.web. The context.ConfigurableWebApplicationContext class exists in spring-web.//These two jars are indirectly introduced by spring-boot-starter-web, so they will not take this branch for ( String className: SERVLET_INDICATOR_CLASSES) { if (!ClassUtils .isPresent(className, null )) { return WebApplicationType.NONE; } } //So it will return from here return WebApplicationType.SERVLET; } Copy code

Let's look at the introduction of the jar package

Go back to the call of new SpringApplication(primarySources).run(args) to see the code of the run method

public ConfigurableApplicationContext run ( String ... args ) { ....... try { ...... //Let s look at the creation of this context, context=new AnnotationConfigServletWebServerApplicationContext() Let s look at the execution of this block in detail. context = createApplicationContext(); ...... //The next few parts will explain this method refreshContext(context); ...... } catch (Throwable ex) { ...... } try { ....... } catch (Throwable ex) { ....... } return context; } Copy code

createApplicationContext() will call the following methods in turn

protected ConfigurableApplicationContext createApplicationContext () { //Here this.webApplicationType is the above WebApplicationType.SERVLET return this .applicationContextFactory.create( this .webApplicationType); } Copy code
//This lambda expression will eventually be called, and the input parameter is the above WebApplicationType.SERVLET ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch (webApplicationType) { case SERVLET: //will return from here return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default : return new AnnotationConfigApplicationContext(); } } catch (Exception ex) { throw new IllegalStateException( "Unable create a default ApplicationContext instance, " + "you may need a custom ApplicationContextFactory" , ex); } }; Copy code

At this point, our context has been created, and this piece of code is relatively simple. Don't say much

2. Find ServletWebServerFactory

Go back to the call of new SpringApplication(primarySources).run(args) again to see the code of the run method

public ConfigurableApplicationContext run ( String ... args ) { ....... try { ...... //The context has been explained above, context=new AnnotationConfigServletWebServerApplicationContext() context = createApplicationContext(); ...... //Let s look at this method below refreshContext(context); ...... } catch (Throwable ex) { ...... } try { ....... } catch (Throwable ex) { ....... } return context; } Copy code

Click to refreshContext(context)

private void refreshContext ( ConfigurableApplicationContext context ) { if ( this .registerShutdownHook) { shutdownHook.registerApplicationContext(context); } refresh(context); } Copy code

Continue to refresh(context)

protected void refresh ( ConfigurableApplicationContext applicationContext ) { //The applicationContext here is the object of AnnotationConfigServletWebServerApplicationContext. Since this class does not have a refresh method, it will jump to the method of its parent class ServletWebServerApplicationContext, we continue to click in applicationContext.refresh(); } Copy code

Point to the refresh method of ServletWebServerApplicationContext

public final void refresh() throws BeansException, IllegalStateException { try { //Continue to jump to the parent class AbstractApplicationContext method super .refresh(); } catch (RuntimeException ex) { WebServer webServer = this .webServer; if (webServer != null ) { webServer.stop(); } throw ex; } } Copy code

Open the refresh method of AbstractApplicationContext

//Most of the initialization work of springboot is done here, but this is not our local focus, we skip public void refresh() throws BeansException, IllegalStateException { synchronized ( this .startupShutdownMonitor) { ...... try { ...... //Continue to point to this method, here will jump to the method of the ServletWebServerApplicationContext class onRefresh(); ..... } catch (BeansException ex) { ..... } finally { ..... } } } Copy code

Open the onRefresh method of ServletWebServerApplicationContext

protected void onRefresh () { super .onRefresh(); try { //Here is our focus this time, we will create a specific web container here, let s click in and see, it is still a method of the ServletWebServerApplicationContext class createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException( "Unable to start web server" , ex); } } Copy code

Open the createWebServer method of ServletWebServerApplicationContext

private void createWebServer () { WebServer webServer = this .webServer; ServletContext servletContext = getServletContext(); //The first time you come in webServer servletContext is null, you will enter the if branch if (webServer == null && servletContext == null ) { //This is just a mark, don t pay attention, skip StartupStep createWebServer = this .getApplicationStartup () .start ( "spring.boot.webserver.create" ); //Here we will find ServletWebServerFactory, which is the factory of the web container. For details, look at the getWebServerFactory() method or the method of the ServletWebServerApplicationContext class ServletWebServerFactory factory = getWebServerFactory(); createWebServer.tag( "factory" , factory.getClass().toString()); this .webServer = factory.getWebServer(getSelfInitializer()); createWebServer.end(); getBeanFactory().registerSingleton( "webServerGracefulShutdown" , new WebServerGracefulShutdownLifecycle( this .webServer)); getBeanFactory().registerSingleton( "webServerStartStop" , new WebServerStartStopLifecycle( this , this .webServer)); } else if (servletContext != null ) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException( "Cannot initialize servlet context" , ex); } } initPropertySources(); } Copy code

Open the getWebServerFactory method of ServletWebServerApplicationContext

protected ServletWebServerFactory getWebServerFactory () { //Use bean names so that we don't consider the hierarchy //Find the definition of the ServletWebServerFactory type of bean from the beanFactory and return the corresponding bean name String [] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0 ) { throw new ApplicationContextException( "Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean." ); } if (beanNames.length> 1 ) { throw new ApplicationContextException( "Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans:" + StringUtils.arrayToCommaDelimitedString(beanNames)); } //Here will be returned from beanFactory bean name is beanNames[0], type is ServletWebServerFactory.class bean object, if the current bean has not been created, then it will create the bean object and return return getBeanFactory().getBean( beanNames[ 0 ], ServletWebServerFactory.class); } Copy code

Can't tell from the above code what the actual ServletWebServerFactory object is? Let's take everyone through this part of loading briefly. Here is the process of springboot loading beans. This part of the logic is more, so I won't expand it in detail this time. I will write another post about the process plan of springboot loading beans.

During the startup process, springboot will load in the file META-INF/spring.factories under the current classpath, the attribute of key=org.springframework.boot.autoconfigure.EnableAutoConfiguration will be loaded as the bean definition, and will be used in this process The properties of key=org.springframework.boot.autoconfigure.AutoConfigurationImportFilterfilter are used as filters, and these classes are filtered in conjunction with META-INF/spring-autoconfigure-metadata.properties to eliminate non-compliant classes (subsequently, according to the annotations on the class) Determine whether to continue to exclude).

Currently these are mainly in the file spring-boot-autoconfigure-2.5.1.jar

Let s take a screenshot of the two parts mentioned above. You can see that there are only 3 filters here. I won t discuss the details. The automatically imported classes are the ones below and filtered out.

# Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ...... #The following will be used in creating servelt, we will pay attention to it in the next part org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ #The following is what we need to use org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ ...... org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ ...... Copy code

Let's look at the class org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration above. This class will not be removed in the web scene. Will be loaded. Let s take a look at this category, we just look at the head.

Here we see the Import annotation on the class, we will continue to import these classes,

ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class Copy code

These three are all related this time. They are all internal classes of ServletWebServerFactoryConfiguration. Let's go in and have a look. The structure of the classes is the same. Let's take a look at the ServletWebServerFactoryConfiguration.EmbeddedTomcat class.

You can see that there are ConditionalOnClass and ConditionalOnMissingBean annotations on EmbeddedTomcat.

Simply put, ConditionalOnClass means that there is a corresponding class in the current class path is loaded

ConditionalOnMissingBean means to load if there is no corresponding type bean definition in the current beanFactory

Multiple conditions are in the relationship of and, and if one condition is not established, subsequent processing will not be performed.

Here the two conditions of the EmbeddedTomcat class are established, then it will continue to traverse all the methods of the current class, find the @Bean annotated method, and load it into the beanFactory

And if the two conditions of EmbeddedJetty and EmbeddedUndertow are not established, the subsequent execution will not be carried out and eliminated

Here will load the EmbeddedTomcat.tomcatServletWebServerFactory method, the return value is TomcatServletWebServerFactory type, we look at the inheritance diagram of the TomcatServletWebServerFactory class, we can see that it happens to inherit the ServletWebServerFactory interface.

Open the getWebServerFactory method of ServletWebServerApplicationContext again

protected ServletWebServerFactory getWebServerFactory () { ....... //So the logic here will actually execute the tomcatServletWebServerFactory method of the ServletWebServerFactoryConfiguration.EmbeddedTomcat class, return the TomcatServletWebServerFactory object, related property injection, etc. I will not talk about return getBeanFactory().getBean(beanNames[ 0 ], ServletWebServerFactory.class); } Copy code

At this point, the search for the entire ServletWebServerFactory is complete

3. Create DispatcherServletRegistrationBean, DispatcherServlet

Look at the META-INF/spring.factories file above

# Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ...... #Now let s focus on this class org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ ...... org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ ...... Copy code

Let's open the org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration class to see

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration(proxyBeanMethods = false ) //We currently only pay attention to the ConditionalOnWebApplication and ConditionalOnClass annotations //ConditionalOnWebApplication is to judge whether the specified class exists according to the type //The current type is Type.SERVLET, to find org.springframework.web.context Does the .support.GenericWebApplicationContext class exist? This class exists in spring-web, so this condition is true @ConditionalOnWebApplication(type = Type.SERVLET) //As mentioned above in this annotation, it is to find whether the specified class exists, this is to find whether DispatcherServlet.class exists, and it will also return true here. @ConditionalOnClass(DispatcherServlet.class) //The above two conditions are both true, the subsequent operations will be performed to traverse the internal classes and methods @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) public class DispatcherServletAutoConfiguration { /** * The bean name for a DispatcherServlet that will be mapped to the root URL "/". */ public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet" ; /** * The bean name for a ServletRegistrationBean for the DispatcherServlet "/". */ public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration" ; @Configuration(proxyBeanMethods = false ) //Here is still a condition. By implementing the Condition interface, it is judged by the matches method. //The DefaultDispatcherServletCondition class is in the current file, and the result of the matches judgment is also true @Conditional(DefaultDispatcherServletCondition.class) //ServletRegistration.class this class exists in tomcat-embed-core, this result is also true @ConditionalOnClass(ServletRegistration.class) //The above two conditions are met, the subsequent operations will be executed to traverse the internal classes and methods @EnableConfigurationProperties(WebMvcProperties.class) protected static class DispatcherServletConfiguration { //beanFactory will create the definition of this DispatcherServletbean, the name of the bean is dispatcherServlet @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet ( WebMvcProperties webMvcProperties ) { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails()); return dispatcherServlet; } @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver ( MultipartResolver resolver ) { //Detect if the user has created a MultipartResolver but named it incorrectly return resolver; } } @Configuration(proxyBeanMethods = false ) //Same as above, don t talk about it @Conditional(DispatcherServletRegistrationCondition.class) //Same as above, let's not talk about it @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) //Here will be looking for DispatcherServletConfiguration.class, and execute the process of loading bean definition, this is the above class @Import(DispatcherServletConfiguration.class) protected static class DispatcherServletRegistrationConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) //ConditionalOnBean finds whether there is a definition of the specified bean. This method needs to inject parameters and needs this class. Currently here is the definition of the dispatcherServlet method above, and it also exists here @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) //DispatcherServlet dispatcherServlet is the bean defined by the dispatcherServlet method. When the DispatcherServletRegistrationBean bean is created, it will look for the existence of dispatcherServlet. If it does not exist, first create the dispatcherServlet bean, and then create DispatcherServletRegistrationBean public DispatcherServletRegistrationBean dispatcherServletRegistration ( dispatcherServletRegistrationBean WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig ) { DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); multipartConfig.ifAvailable(registration::setMultipartConfig); return registration; } } ...... } Copy code

The above is the process of creating DispatcherServlet, DispatcherServletRegistrationBean

4. Create tomcat, load Servlet.class, filter.class, listener

Go back to the createWebServer method of ServletWebServerApplicationContext again

private void createWebServer () { WebServer webServer = this .webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create"); //factory TomcatServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); // createWebServer.tag("factory", factory.getClass().toString()); //tomcat getSelfInitializer() lambda this.webServer = factory.getWebServer(getSelfInitializer()); createWebServer.end(); getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer)); getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer)); } else if (servletContext != null ) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException( "Cannot initialize servlet context" , ex); } } initPropertySources(); } Copy code
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer () { return this ::selfInitialize; } //Is the parameter to create webServer private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans: getServletContextInitializerBeans()) { beans.onStartup(servletContext); } } Copy code

factory.getWebServer(getSelfInitializer()) will call the getWebServer method of TomcatServletWebServerFactory

public WebServer getWebServer ( ServletContextInitializer... initializers ) { ....... //The above participation will be passed on here prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); } Copy code

Click into prepareContext(tomcat.getHost(), initializers) to see

protected void prepareContext ( Host host, ServletContextInitializer[] initializers ) { ...... ServletContextInitializer[] initializersToUse = mergeInitializers(initializers); host.addChild(context); //Continue to pass on configureContext(context, initializersToUse); postProcessContext(context); } Copy code

Then click the configureContext(context, initializersToUse) call to see

protected void configureContext ( Context context, ServletContextInitializer[] initializers ) { //will be passed to TomcatStarter as a construction parameter, let's go here to see TomcatStarter starter = new TomcatStarter(initializers); ...... } Copy code

Let's take a look at how the TomcatStarter class uses the initializers construction parameter.

This class is not long class TomcatStarter implements ServletContainerInitializer { ...... TomcatStarter ( ServletContextInitializer[] initializers ) { //Join the conference as its member attribute this .initializers = initializers; } @Override public void onStartup( Set <Class<?>> classes, ServletContext servletContext) throws ServletException { try { for (ServletContextInitializer initializer: this .initializers) { //The onStartup method will be called here, the input parameter here is the object of ApplicationContextFacade, inside The ApplicationContext is wrapped, and then the TomcatEmbeddedContext is wrapped. This is related to tomcat. The following screenshot is the object structure of the servletContext initializer.onStartup(servletContext); } } catch (Exception ex) { ...... } Copy code

The above initializer.onStartup(servletContext) will call the selfInitialize method of ServletWebServerApplicationContext

private void selfInitialize(ServletContext servletContext) throws ServletException { //Here is to set ApplicationContextFacade to the current servletContext prepareWebApplicationContext(servletContext); //Here is the scope of application registration in beanFactory registerApplicationScope(servletContext); //Here is the registration of context-related beans WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); //Let s focus on here. getServletContextInitializerBeans() is to define a ServletContextInitializerBeans object. Let s click in and see for (ServletContextInitializer beans: getServletContextInitializerBeans()) { beans.onStartup(servletContext); } } Copy code
protected Collection<ServletContextInitializer> getServletContextInitializerBeans () { //Here getBeanFactory() is the global beanFactory return new ServletContextInitializerBeans(getBeanFactory()); } Copy code
public ServletContextInitializerBeans ( ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes ) { this .initializers = new LinkedMultiValueMap<>(); //Since we did not pass the initializerTypes parameter, there is only the ServletContextInitializer.class in this.initializerTypes this .initializerTypes = ( initializerTypes.length != 0 )? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class); //Here is mainly to find this.initializerTypes type from beanFactory, let's go in and have a look, it is the following method addServletContextInitializerBeans(beanFactory); addAdaptableBeans(beanFactory); List<ServletContextInitializer> sortedInitializers = this .initializers.values().stream() .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE)) .collect(Collectors.toList()); this .sortedList = Collections.unmodifiableList(sortedInitializers); logMappings( this .initializers); } private void addServletContextInitializerBeans ( ListableBeanFactory beanFactory ) { for (Class<? extends ServletContextInitializer> initializerType: this .initializerTypes) { //By default, only the beans corresponding to the DispatcherServletRegistrationBean in Part 3 above are found here for (Entry< String ,? extends ServletContextInitializer > initializerBean: getOrderedBeansOfType(beanFactory, initializerType)) { //The key here is the name of the bean, and the value is the bean object. When you go in and take a look, it is the following method addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory); } } } private void addServletContextInitializerBean ( String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory ) { //Will come to this branch if (initializer instanceof ServletRegistrationBean) { //The servlet returned here is also the bean corresponding to Part 3 DispatcherServlet Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet(); //Click again addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source); } else if (initializer instanceof FilterRegistrationBean) { Filter source = ((FilterRegistrationBean<?>) initializer).getFilter(); addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); } else if (initializer instanceof DelegatingFilterProxyRegistrationBean) { String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName(); addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source); } else if (initializer instanceof ServletListenerRegistrationBean) { EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener(); addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source); } else { addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory, initializer); } } private void addServletContextInitializerBean ( Class<?> type, String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory, Object source ) { //initializers here is a map, according to the type, bean objects are loaded, here type is javax.servlet.Servlet.class, value is the above DispatcherServletRegistrationBean this .initializers.add(type, initializer) ; if (source != null ) { //Mark the underlying source as seen in case it wraps an existing bean //Add the bean corresponding to DispatcherServlet here this .seen.add(source); } if (logger.isTraceEnabled()) { String resourceDescription = getResourceDescription(beanName, beanFactory); int order = getOrder(initializer); logger.trace( "Added existing " + type.getSimpleName() + " initializer bean'" + beanName + "'; order=" + order + ", resource=" + resourceDescription); } } Copy code

Go back to the construction method of ServletContextInitializerBeans, and then look at the following

public ServletContextInitializerBeans ( ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes ) { ...... //The content here has been read above, let s look at the following sentence now, click on it addAdaptableBeans(beanFactory); List<ServletContextInitializer> sortedInitializers = this .initializers.values().stream() .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE)) .collect(Collectors.toList()); this .sortedList = Collections.unmodifiableList(sortedInitializers); logMappings( this .initializers); } Copy code
protected void addAdaptableBeans ( ListableBeanFactory beanFactory ) { //Don t pay attention to this sentence MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory); //Don't pay attention to this sentence addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig)); //Click here to see addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter()); for (Class<?> listenerType: ServletListenerRegistrationBean.getSupportedTypes()) { addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType, new ServletListenerRegistrationBeanAdapter()); } } Copy code
private <T, B extends T> void addAsRegistrationBean ( ListableBeanFactory beanFactory, Class<T> type, Class<B> beanType, RegistrationBeanAdapter<T> adapter ) { //The beanType here is Filter.class, the following sentence is to get all the bean List< Map .Entry< String , B>> from the beanFactory. entries = getOrderedBeansOfType(beanFactory, beanType, this .seen); for (Entry< String , B> entry: entries) { String beanName = entry.getKey(); B bean = entry.getValue(); //Place the bean in this.seen if ( this .seen.add(bean)) { //One that we haven't already seen //Packed into a RegistrationBean object RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size()); int order = getOrder(bean); registration.setOrder(order); //Also this.initializers placed inside the this .initializers.add (type, Registration); IF (logger.isTraceEnabled ()) { logger.trace( "Created " + type.getSimpleName() + " initializer for bean'" + beanName + "'; order=" + order + ", resource=" + getResourceDescription(beanName, beanFactory)); } } } } Copy code

Go back to the addAdaptableBeans method above and look at the back

protected void addAdaptableBeans ( ListableBeanFactory beanFactory ) { ...... //I have just said it here.//The following part is not mentioned, here is basically the same as above, but the processing type becomes ServletContextAttributeListener.class, ServletRequestListener.class, ServletRequestAttributeListener.class, HttpSessionAttributeListener.class , HttpSessionIdListener.class, HttpSessionListener.class, ServletContextListener.class these types for (Class<?> listenerType: ServletListenerRegistrationBean.getSupportedTypes()) { addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType, new ServletListenerRegistrationBeanAdapter()); } } Copy code

Go back to the construction method of ServletContextInitializerBeans, and then look at the following

public ServletContextInitializerBeans ( ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes ) { ...... //I have said it just now, look at the following //Here is to put all the related beans obtained above into this.sortedList, below I am a screenshot of my local this.sortedList List<ServletContextInitializer > sortedInitializers = this .initializers.values().stream() .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE)) .collect(Collectors.toList()); this .sortedList = Collections.unmodifiableList(sortedInitializers); logMappings( this .initializers); } Copy code

Here the construction method of ServletContextInitializerBeans is complete, and then look back to see the definition of this class

public class ServletContextInitializerBeans extends AbstractCollection < ServletContextInitializer > copy the code

This class inherits the AbstractCollection class, then it needs to implement the following abstract method

public abstract Iterator<E> iterator(); Copy code

Let's look at the iterator method of ServletContextInitializerBeans

@Override public Iterator<ServletContextInitializer> iterator () { return this .sortedList.iterator(); } Copy code

See it, this is this.sortedList.iterator() that returns the above

We return to the selfInitialize method of ServletWebServerApplicationContext again

private void selfInitialize(ServletContext servletContext) throws ServletException { ...... //I have mentioned it here //getServletContextInitializerBeans() This method is to construct ServletContextInitializerBeans //The for loop here is also the method of calling the iterator of ServletContextInitializerBeans. In fact, the traversal is the above this. sortedList for (ServletContextInitializer beans: getServletContextInitializerBeans()) { //Here is to add the Servlet.class, filter.class, listener, etc. found in the beanFactory to the tomcat container, we will only go into the servlet to see //Go to DispatcherServletRegistrationBean to have a look beans.onStartup(servletContext); } } Copy code
//This method is in RegistrationBean, the parent class of DispatcherServletRegistrationBean //All Servlet.class, filter.class, listeners will go here public final void onStartup(ServletContext servletContext) throws ServletException { //Here is the expression returned, don t pay attention String description = getDescription(); if (!isEnabled()) { logger.info(StringUtils.capitalize(description) + "was not registered (disabled)" ); return ; } //Here is implemented by different subclasses, DispatcherServletRegistrationBean will be called to ServletRegistrationBean register(description, servletContext); } Copy code
//This method is in the ServletRegistrationBean class @Override protected ServletRegistration.Dynamic addRegistration ( String description, ServletContext servletContext ) { String name = getServletName(); //The object of ApplicationContextFacade above the servletContext here, here will load the bean object of DispatcherServlet into TomcatEmbeddedContext, and all subsequent http requests are at the end Will flow to DispatcherServlet for specific distribution return servletContext.addServlet(name, this ); } Copy code

Here Servlet.class, filter.class, listeners are all loaded into tomcat

5. Create RequestMappingHandlerMapping

Look at the META-INF/spring.factories file above

# Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ...... org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ ...... #Now let s focus on this class org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ ...... Copy code

The specific loading is similar to the above part, so I won t expand it, just look at what we need

//Here will create RequestMappingHandlerMapping bean @Bean @Primary @Override public RequestMappingHandlerMapping requestMappingHandlerMapping ( @Qualifier( "mvcContentNegotiationManager" ) ContentNegotiationManager contentNegotiationManager, @Qualifier( "mvcConversionService" ) FormattingConversionService conversionService, @Qualifier( "mvcResourceUrlProvider" ) ResourceUrlProvider resourceUrlProvider ) { //Must be @Primary for MvcUriComponentsBuilder to work return super .requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider); Copy code

Look at the inheritance diagram of this class

RequestMappingHandlerMapping implements the InitializingBean interface, and will call the afterPropertiesSet method in the invokeInitMethods method after the bean object is created, and finally call the afterPropertiesSet method of the AbstractHandlerMethodMapping

@Override public void afterPropertiesSet () { initHandlerMethods(); } /** * Scan beans in the ApplicationContext, detect and register handler methods. * @see #getCandidateBeanNames() * @see #processCandidateBean * @see #handlerMethodsInitialized */ protected void initHandlerMethods () { //Find all beans in beanFactory to traverse here for ( String beanName: getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { //Click here to see processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); } Copy code
protected void processCandidateBean ( String beanName ) { Class<?> beanType = null ; try { //Acquire the Class object of the corresponding bean according to beanName beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { //An unresolvable bean type, probably from a lazy bean-let's ignore it. if (logger.isTraceEnabled()) { logger.trace( "Could not resolve type for bean'" + beanName + "'" , ex); } } //Determine whether there is Controller.class on the class, RequestMapping.class annotation if (beanType != null && isHandler(beanType)) { //Here, all methods on beanName will be parsed and traversed to find methods annotated with RequestMapping.class , Create a RequestMappingInfo object and place it in the registry attribute (in AbstractHandlerMethodMapping), so that all the http requests in our defined controllers will be scanned detectHandlerMethods(beanName); } } Copy code

6. Load RequestMappingHandlerMapping into DispatcherServlet

When we first request, we will execute the initStrategies method of DispatcherServlet, this method will only be executed once

protected void initStrategies ( ApplicationContext context ) { ...... This will load the controller we defined in the RequestMappingHandlerMapping found before initHandlerMappings(context); ...... } Copy code

Will call here

private void initHandlerMappings ( ApplicationContext context ) { this .handlerMappings = null ; if ( this .detectAllHandlerMappings) { //Find all HandlerMappings in the ApplicationContext, including ancestor contexts. //Here will find all HandlerMapping.class beans in beanFactory, which contains our Part 5 RequestMappingHandlerMapping Map < String , HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true , false ); if (!matchingBeans.isEmpty()) { //Place all found in the handlerMappings this .handlerMappings = new ArrayList<>(matchingBeans.values() ); //We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort( this .handlerMappings); } } ...... } Copy code

When our browser requests, we will eventually go to the doDispatch method of DispatcherServlet, process our request and return, just take a look

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ...... try { ModelAndView mv = null ; Exception dispatchException = null ; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); //Determine handler for the current request. //Here, the request path of the request will be requested to find the method of the actual controller to be executed, click in and have a look mappedHandler = getHandler(processedRequest); ...... } Copy code
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if ( this .handlerMappings != null ) { //This is actually to find the corresponding path based on the traversal. This.handlerMappings is the value assigned in the initHandlerMappings method for (HandlerMapping mapping: this .handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null ) { return handler; } } } return null ; } Copy code

At this point, the whole process of loading the web container by springboot is basically completed. There are many things involved in this part, so it may be relatively crude. Forgive me.