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