preface

Spring Boot load Web container Tomcat process source code analysis

My native Springboot version is 2.5.1, which will be the basis for the rest of the analysis

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

We do this by introducing it in poM files

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

To introduce the Web container, the default is Tomcat.

This article mainly describes the spring Boot loading web container Tomcat part, in order to avoid too scattered knowledge points, other relevant such as bean loading, Tomcat internal process is not discussed in depth, Spring Boot actual combat learning notes.

1, in springboot web projects, the global context is AnnotationConfigServletWebApplicationContext

Now, let’s look at the next part

First of all, this is how our entry code is written

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

Jumping to the run method calls the following two methods in turn

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

We first create a SpringApplication instance object, jump to the Constructor of the SpringApplication, and call the following methods

public SpringApplication(Class
       ... primarySources) {
		this(null, primarySources);
	}
Copy the code
@SuppressWarnings({ "unchecked"."rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class
       ... primarySources){...// This time all the irrelevant code is omitted, only relevant code is retained
   / / this here. WebApplicationType = webApplicationType. SERVLET, we have to analyze the code under the implementation of the specific assignments
   this.webApplicationType = WebApplicationType.deduceFromClasspath(); . }Copy the code

Continue to jump to WebApplicationType. DeduceFromClasspath () go and see

// This method checks for the existence of the specified class in the current classpath and returns the enumeration type
	static WebApplicationType deduceFromClasspath() {
	// WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
	
	// We introduce spring-boot-starter-web through the POM file, which introduces spring-webMVC. The above class is in the webMVC, so it does not enter the 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. The servlet class exists in tomcat-embed-core
    / / org. Springframework. Web. Context. ConfigurableWebApplicationContext this class exists in spring - web
    // Both jars are indirectly introduced by spring-boot-starter-web, so this branch is not used either
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if(! ClassUtils.isPresent(className,null)) {
				returnWebApplicationType.NONE; }}// So it will return from here
		return WebApplicationType.SERVLET;
	}
Copy the code

Let’s look at the introduction of jar packages

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

public ConfigurableApplicationContext run(String. args){...try{.../ / let's take a look at creating in this context, the context = new AnnotationConfigServletWebServerApplicationContext () to the implementation of specific see the belowcontext = createApplicationContext(); .// This method will be explained in the following sectionsrefreshContext(context); . }catch (Throwable ex) {
			......
		}

		try{... }catch (Throwable ex) {
		.......
		}
		return context;
	}
Copy the code

CreateApplicationContext (), in turn, calls the following methods

protected ConfigurableApplicationContext createApplicationContext() {
		/ / here enclosing webApplicationType is above webApplicationType. SERVLET
		return this.applicationContextFactory.create(this.webApplicationType);
	}
Copy the code
/ / will call to the lambda expressions, the ginseng 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 newAnnotationConfigApplicationContext(); }}catch (Exception ex) {
			throw new IllegalStateException("Unable create a default ApplicationContext instance, "
					+ "you may need a custom ApplicationContextFactory", ex); }};Copy the code

At this point, our context has been created, and this code is relatively simple. What more can we say

2. Find the ServletWebServerFactory

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

public ConfigurableApplicationContext run(String. args){...try{.../ / the above have done on the context, the context = new AnnotationConfigServletWebServerApplicationContext ()context = createApplicationContext(); .// Let's look at this methodrefreshContext(context); . }catch (Throwable ex) {
			......
		}

		try{... }catch (Throwable ex) {
		.......
		}
		return context;
	}
Copy the code

Point to the refreshContext (context)

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

Go ahead and hit Refresh (Context)

protected void refresh(ConfigurableApplicationContext applicationContext) {
        / / here is applicationContext AnnotationConfigServletWebServerApplicationContext object, because this kind of no refresh method, Will jump to its superclass ServletWebServerApplicationContext method, we continue to point in
		applicationContext.refresh();
	}
Copy the code

Point to the ServletWebServerApplicationContext refresh method

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();
			}
			throwex; }}Copy the code

Open the AbstractApplicationContext refresh method

// Most of the initialization of SpringBoot is done here, but this is not our local focus and we will skip all the irrelevant ones
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			......
			try{.../ / continue to point to this method, will jump to ServletWebServerApplicationContext methods of this classonRefresh(); . }catch (BeansException ex) {
			.....
			}

			finally{... }}}Copy the code

Open the ServletWebServerApplicationContext onRefresh method

protected void onRefresh() {
		super.onRefresh();
		try {
			/ / here is our focus, this will create specific web container, here we go in, or ServletWebServerApplicationContext the methods of this class
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex); }}Copy the code

Open the ServletWebServerApplicationContext createWebServer method

private void createWebServer() {
	
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		// The first time the webServer servletContext is null, it goes into the if branch
		if (webServer  == null && servletContext == null) {
		   // This is just a mark, don't pay attention to, skip
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
			/ / here will be to find ServletWebServerFactory, namely web container factory, specific see getWebServerFactory () method, or ServletWebServerApplicationContext the methods of this 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 the code

Open the ServletWebServerApplicationContext getWebServerFactory method

protected ServletWebServerFactory getWebServerFactory() {
		// Use bean names so that we don't consider the hierarchy
		// Find the definition of a bean of type ServletWebServerFactory from beanFactory and return the name of the bean
		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 return from the beanFactory bean name for beanNames [0], a type of ServletWebServerFactory. The bean class object, if the current bean has not been created and is now will create bean object and return
		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
	}
Copy the code

You can’t tell from the above code what the actual ServletWebServerFactory object is. Here is the specific springboot loading bean process, this part of the logic is more, this time will not be specific. The process plan for springBoot loading beans will be covered in a follow-up article.

In the meta-INF /spring.factories file under the current classpath, Key = org. Springframework. Boot. Autoconfigure. EnableAutoConfiguration attribute as a bean definition to load, In the process also use key = org. Springframework. Boot. Autoconfigure. AutoConfigurationImportFilterfilter attributes as a filter, Use meta-INF /spring-autoconfigure-metadata.properties to filter these classes and remove any classes that do not meet the requirements.

These are currently in the spring-boot-autoconfigure-2.5.1. Jar file

The following is a cut of the above two parts, you can see that there are three filters here, the specific discussion will not be expanded, the automatic import class is the following filter removed

# 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=\ ...... # this is used to create servlets, The part we will focus on it org. Springframework. Boot. Autoconfigure. Web. Servlet. DispatcherServletAutoConfiguration, \ # below this is what we need org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ ...... org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ ......Copy the code

We look at the above org. Springframework. Boot. Autoconfigure. Web. Servlet. ServletWebServerFactoryAutoConfiguration this class, the class in the web scenarios, They don’t get thrown out. Will be loaded. So let’s look at this class, let’s just look at the head

Here we see an Import annotation on the class, and we’ll continue to Import these classes,

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

These three are the related, they are all ServletWebServerFactoryConfiguration inner classes, we look inside, the structure of the class are all the same. We’ll look at ServletWebServerFactoryConfiguration. EmbeddedTomcat class

ConditionalOnClass ConditionalOnMissingBean ConditionalOnMissingBean ConditionalOnMissingBean

ConditionalOnClass ConditionalOnClass ConditionalOnClass ConditionalOnClass ConditionalOnClass ConditionalOnClass ConditionalOnClass ConditionalOnClass

ConditionalOnMissingBean is said there is no corresponding types in the current the beanFactory to load bean definitions

Multiple conditions are related to and. If one condition is not valid, subsequent processing will not be carried out.

Here both conditions are true for the EmbeddedTomcat class, which then continues through all the methods of the current class, finding the @Bean-annotated method and loading it into the beanFactory

The EmbeddedJetty and EmbeddedUndertow conditions were not established, so the subsequent execution would not be carried out and the EmbeddedUndertow conditions were removed

Here will EmbeddedTomcat. TomcatServletWebServerFactory this method to load, the return value is tomcatServletWebServerFactory type, We see TomcatServletWebServerFactory class inheritance graph, you can see it is inherited ServletWebServerFactory interface.

Open ServletWebServerApplicationContext getWebServerFactory method again

protected ServletWebServerFactory getWebServerFactory(){.../ / so the logic will actually perform ServletWebServerFactoryConfiguration. EmbeddedTomcat tomcatServletWebServerFactory method in a class, Return TomcatServletWebServerFactory object, the relevant properties of injection and so on is not about here
		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
	}
Copy the code

At this point, the entire ServletWebServerFactory lookup is complete

3, create DispatcherServletRegistrationBean, DispatcherServlet

Take another 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 we focus on this kind of org springframework. Boot. The autoconfigure. Web. Servlet. DispatcherServletAutoConfiguration, \ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ ...... org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ ......Copy the code

We open the org. Springframework. Boot. Autoconfigure. Web. Servlet. DispatcherServletAutoConfiguration this class and have a look

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
/ / we only focus on the current ConditionalOnWebApplication, ConditionalOnClass annotation
/ / ConditionalOnWebApplication is judged according to the type specified class exists
/ / the current type is the SERVLET, is to find the org. Springframework. Web. Context. Support. GenericWebApplicationContext class exists, this class exists in spring - the web, So this condition is true
@ConditionalOnWebApplication(type = Type.SERVLET)
// This annotation looks for the presence of the specified class, this one looks for the presence of dispatcherservlet. class, which also returns true
@ConditionalOnClass(DispatcherServlet.class)
// If both conditions are true, the following operations are performed to traverse the inner 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)
    // The Condition interface is implemented using the matches method
    / / DefaultDispatcherServletCondition this class in the current file, matches judgment result is also true
	@Conditional(DefaultDispatcherServletCondition.class)
    / / ServletRegistration. Class this class exists in tomcat - embed - inside the core, the result is also true
	@ConditionalOnClass(ServletRegistration.class)
    // If the above two conditions are true, subsequent operations are performed to traverse the inner class and method
	@EnableConfigurationProperties(WebMvcProperties.class)
	protected static class DispatcherServletConfiguration {

        //beanFactory creates the definition of the DispatcherServletbean. The bean name 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)
    // As above, no more
	@Conditional(DispatcherServletRegistrationCondition.class)
    // As above, no more
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
    / / here will find DispatcherServletConfiguration. Class, and perform the loading beans defined process, this is the upper class
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        ConditionalOnBean () ConditionalOnBean () ConditionalOnBean () ConditionalOnBean () ConditionalOnBean () ConditionalOnBean () ConditionalOnBean () ConditionalOnBean () ConditionalOnBean
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        / / DispatcherServlet DispatcherServlet is the DispatcherServlet this method defines the bean, when creating DispatcherServletRegistrationBean this bean, Will go to find the dispatcherServlet exists, if not, create dispatcherServlet this bean, first create DispatcherServletRegistrationBean again
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider
       
         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);
			returnregistration; }}... }Copy the code

The above is to create DispatcherServlet, DispatcherServletRegistrationBean process

4, create a tomcat, load the Servlet class, filter. The class, the listener

Once again return to ServletWebServerApplicationContext createWebServer method

private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
			/ / we've seen here above, the factory is TomcatServletWebServerFactory an instance of a class object
			ServletWebServerFactory factory = getWebServerFactory();
            // Make a mark here, don't pay attention
            createWebServer.tag("factory", factory.getClass().toString());
            The input parameter getSelfInitializer() is a lambda expression. This is important
			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 the code
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
	}
	// is an argument to create a webServer
	private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
		for(ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); }}Copy the code

Factory. GetWebServer (getSelfInitializer () will call to TomcatServletWebServerFactory getWebServer method

public WebServer getWebServer(ServletContextInitializer... initializers){...// The above entry is passed down here
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}
Copy the code

Go into prepareContext(tomcat.gethost (), initializers)

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

Go to configureContext(Context, initializersToUse)

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

Let’s take a look at how TomcatStarter can use initializers.

This class is not longclass TomcatStarter implements ServletContainerInitializer {...TomcatStarter(ServletContextInitializer[] initializers) {
        // Join the conference as its member property
		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, where the input is the ApplicationContextFacade object that wraps the ApplicationContext, which wraps the TomcatEmbeddedContext, This is associated with Tomcat. The following screenshot shows the object structure of a servletContextinitializer.onStartup(servletContext); }}catch (Exception ex) {
			......
	}
Copy the code

The above, initializer. OnStartup (servletContext) will call to ServletWebServerApplicationContext selfInitialize method

private void selfInitialize(ServletContext servletContext) throws ServletException {
        // Set the ApplicationContextFacade on the current servletContext
		prepareWebApplicationContext(servletContext);
        // Register application scope in beanFactory
		registerApplicationScope(servletContext);
        // Register context-specific beans again
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        / / we focus perspective here getServletContextInitializerBeans () is to define a ServletContextInitializerBeans object, we go in and have a look
		for(ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); }}Copy the code
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
		// getBeanFactory() is the global beanFactory
		return new ServletContextInitializerBeans(getBeanFactory());
	}
Copy the code
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes) {
		this.initializers = new LinkedMultiValueMap<>();
        / / because we didn't pass initializerTypes this parameter, so this. InitializerTypes ServletContextInitializer only. Inside the class - this class
		this.initializerTypes = (initializerTypes.length ! =0)? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class);InitializerTypes = this.initializerTypes = this.initializerTypes
		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 top part 3 DispatcherServletRegistrationBean here to find the corresponding bean
			for (Entry<String,?extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
					initializerType)) {
                // The key is the name of the bean, and the value is the bean object
				addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
			}
		}
	}
	private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
			ListableBeanFactory beanFactory) {
        // Will go to this branch
		if (initializer instanceof ServletRegistrationBean) {
            // The servlet returned here is also the bean of part 3's DispatcherServletServlet source = ((ServletRegistrationBean<? >) initializer).getServlet();// Click in again
			addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceofFilterRegistrationBean) { 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 instanceofServletListenerRegistrationBean) { 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) {
        / / the initializers here is a map, according to the type, bean object to load, the type is javax.mail servlet. Servlet. The class, the value is the DispatcherServletRegistrationBean above
		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 the 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 the code

Back to ServletContextInitializerBeans construction method, and then look back

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes){...// If you want to go to the library, you can go to the library
		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 the code
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
		// Don't worry about it
		MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
		// Don't worry about it
		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,newServletListenerRegistrationBeanAdapter()); }}Copy the code
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class
       
         type, Class
         beanType, RegistrationBeanAdapter
         
           adapter
         
       ) {
		BeanFactory = beanFactory = beanFactory = beanFactory = beanFactory = beanFactory = beanFactory
		List<Map.Entry<String, B>> 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
                // Wrap as a RegistrationBean object
				RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
				int order = getOrder(bean);
				registration.setOrder(order);
                // Also put in the this.initializers
				this.initializers.add(type, registration);
				if (logger.isTraceEnabled()) {
					logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
							+ order + ", resource="+ getResourceDescription(beanName, beanFactory)); }}}}Copy the code

Go back to the addAdaptableBeans method above, and look at the next one

protected void addAdaptableBeans(ListableBeanFactory beanFactory){...// It was just said here
         // This is basically the same as above. But the type of processing into ServletContextAttributeListener. Class, ServletRequestListener. Class, ServletRequestAttributeListener. Class, HttpS EssionAttributeListener. Class, HttpSessionIdListener. Class, HttpSessionListener. Class, ServletContextListener. The class of these types
		for(Class<? > listenerType : ServletListenerRegistrationBean.getSupportedTypes()) { addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,newServletListenerRegistrationBeanAdapter()); }}Copy the code

Back to ServletContextInitializerBeans construction method, and then look back

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes){...// This is what we have just said
        // This is where we put all of the relevant beans we got above into this.sortedList. Here is my local this.sortedList screenshot
		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 the code

Here ServletContextInitializerBeans constructor was finished, and then go back and look at the definition of a class

public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer>
Copy the code

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

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

The way we look at ServletContextInitializerBeans iterator

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

See, this returns the above this.sortedList.iterator()

We once again return to ServletWebServerApplicationContext selfInitialize method

private void selfInitialize(ServletContext servletContext) throws ServletException {
	......// This is all said above
	/ / getServletContextInitializerBeans () this method is to construct ServletContextInitializerBeans
	/ / the for loop is also called the ServletContextInitializerBeans iterator method, actually traversal is above this. SortedList
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		/ / here is to find in the beanFactory to Servlet. The class of filter. The class, listeners, etc. Are added to the tomcat container, we will only enter into the Servlet to see see
		/ / into the DispatcherServletRegistrationBean inside to seebeans.onStartup(servletContext); }}Copy the code
/ / this method in the superclass RegistrationBean DispatcherServletRegistrationBean
	/ / all the Servlet class, filter. The class, the listener will all come to here
	public final void onStartup(ServletContext servletContext) throws ServletException {
		// This is the return statement
		String description = getDescription();
		if(! isEnabled()) { logger.info(StringUtils.capitalize(description) +" was not registered (disabled)");
			return;
		}
		/ / by different subclasses to achieve here, DispatcherServletRegistrationBean will call into the ServletRegistrationBean
		register(description, servletContext);
	}
Copy the code
// This method is in the class ServletRegistrationBean
	@Override
	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
		String name = getServletName();
		// The servletContext ApplicationContextFacade object, where the DispatcherServlet bean object will be loaded into TomcatEmbeddedContext, All subsequent HTTP requests are eventually routed to the DispatcherServlet for specific distribution
		return servletContext.addServlet(name, this.servlet);
	}
Copy the code

Here the Servlet class, the filter class, all loaded into the tomcat listener

5, create RequestMappingHandlerMapping

Take another 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 we focus on this kind of org springframework. Boot. The autoconfigure. Web. Servlet. WebMvcAutoConfiguration. \...Copy the code

The specific loading is similar to the above part, so I won’t expand it and look directly 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 the code

Take a look at the class inheritance diagram

RequestMappingHandlerMapping InitializingBean interface is achieved, In bean object invokeInitMethods after creation method, call the afterPropertiesSet method will invoke AbstractHandlerMethodMapping afterPropertiesSet method

@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 for traversal
		for (String beanName : getCandidateBeanNames()) {
			if(! beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {// Click here to see
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
Copy the code
protected void processCandidateBean(String beanName){ Class<? > beanType =null;
		try {
			// Get 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); }}// Check whether there are Controller. Class, requestMapping.class annotations
		if(beanType ! =null && isHandler(beanType)) {
			RequestMappingInfo = RequestMappingInfo = RequestMappingInfo = RequestMappingInfo; Placed into the registry attributes (in AbstractHandlerMethodMapping), so we all the HTTP requests will be defined in the controller to be scanneddetectHandlerMethods(beanName); }}Copy the code

6, load RequestMappingHandlerMapping the DispatcherServlet

On our first request, the initStrategies method to the DispatcherServlet is executed only once

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

It’s going to be called 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 in the beanFactory HandlerMapping. The bean class, which contains the RequestMappingHandlerMapping we part 5
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true.false);
			if(! matchingBeans.isEmpty()) {// Place all found names in handlerMappings
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings); }}... }Copy the code

When our browser makes a request, we end up going to the doDispatch method of the DispatcherServlet, processing our request and returning it. Let’s see

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.
                // In this case, the request path of the request is requested to find the actual controller method to executemappedHandler = getHandler(processedRequest); . }Copy the code
@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings ! =null) {
            // The "this.handlerMappings" method is the "initHandlerMappings" method. // The "this.handlerMappings" method is the "initHandlerMappings" method
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if(handler ! =null) {
					returnhandler; }}}return null;
	}
Copy the code

At this point, the whole process of springBoot loading the Web container is basically complete. This part involves a lot of things, so it may be relatively coarse, we forgive you