Spring boot source code analysis - automatic configuration of spring MVC

Posted by axman505 on Sun, 16 Jan 2022 07:56:34 +0100

In the last section, we introduced SpringBoot How to start a built-in tomcat of We know we're here SpringBoot
Projects can be used directly, such as @RequestMapping This kind of SpringMVC Will the students be surprised
Strange, why? I have no configuration SpringMVC Why can it be used?
In fact, it only introduces starter Is not enough, recall, in an ordinary WEB How to use it in the project SpringMVC , I
The first thing we should do is web.xml The following configuration is configured in

 

But in SpringBoot We don't have it web.xml File, how do we configure one Dispatcherservlet And?
actually Servlet3.0 The specification specifies that one should be added Servlet , in addition to using xml There is also a way of configuration through code
The pseudo code is as follows:
servletContext . addServlet ( name , this . servlet );
So that is, if we can go dynamically web Add a container that we have constructed DispatcherServlet Object,
Does it realize automatic assembly SpringMVC Yes

I Automatically configure DispatcherServlet and DispatcherServletRegistry

 

springboot Automatic configuration based on SPI Mechanism, the core point of realizing automatic configuration is to add an automatically configured class,
SpringBoot MVC The automatic configuration of is naturally the same principle.
So, find it first springmvc Corresponding auto configuration class.
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConf
iguration

(1) Dispatcher servlet autoconfiguration autoconfiguration class

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
1 First of all, @Configuration Table name this is a configuration class and will be spring Give me analysis.
2 , @ConditionalOnWebApplication It means that one web Project, and Servlet Will be solved when the project is completed
Analysis.
3 , @ConditionalOnClass to specify DispatcherServlet This core class must exist to resolve it.
4 , @AutoConfigureAfter Indicate in ServletWebServerFactoryAutoConfiguration This class will be solved later
Analysis, set a sequence.
In general, these annotations indicate that the parsed preconditions of the automatic configuration class need to be met.
secondly, DispatcherServletAutoConfiguration Class mainly contains two inner classes, namely
1 , DispatcherServletConfiguration
2 , DispatcherServletRegistrationConfiguration
As the name suggests, the former is configuration DispatcherServlet , the latter is configuration DispatcherServlet Registration class for. What is registration
Class? We know Servlet Instance is to be added (registered) to tomcat In this way ServletContext In order to
Enough to provide the requested service. So, DispatcherServletRegistrationConfiguration A will be generated Bean , responsible for
DispatcherServlet Register to ServletContext Yes.

(2) Configure DispatcherServletConfiguration

Let's see first DispatcherServletConfiguration This configuration class
@Configuration(proxyBeanMethods = false)
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
	protected static class DispatcherServletConfiguration {
@Conditional Indicates a precondition judgment, which is determined by DefaultDispatcherServletCondition realization. Mainly sentenced
Does the break already exist DispatcherServlet , if not, the resolution will be triggered.
@ConditionalOnClass Indicates when ServletRegistration When this class exists, it will trigger parsing and generation
DispatcherServlet To register with ServletContext Yes.
last, @EnableConfigrationProperties Will from application.properties Read from such a configuration file
spring.http and spring.mvc Prefix properties generate configuration objects HttpProperties and WebMvcProperties .
Look again DispatcherServletConfiguration The inner code of this inner class

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
			dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.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;
		}
We are familiar with these two methods, that is, generation Bean .
dispatcherServlet Method generates a DispatcherServlet of Bean Object. Relatively simple, is to get a real
Example, and then add some attribute settings.
multipartResolver The main method is to configure your MultipartResolver of Bean Rename it to prevent you from using it
multipartResolver The name as Bean Your name.

(3) Configure DispatcherServletRegistrationConfiguration

Look at the registration class Bean to configure
@Configuration(proxyBeanMethods = false)
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {
alike, @Conditional There is a pre judgment, DispatcherServletRegistrationCondition Mainly judged the
Registered class Bean Whether it exists.
@ConditionOnClass Also judged ServletRegistration Does it exist
@EnableConfigurationProperties Generated WebMvcProperties Property object for
@Import Imported DispatcherServletConfiguration , that is, the configuration object above.
Look again DispatcherServletRegistrationConfiguration Internal implementation of
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
				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;
		}
There is only one method inside, generated DispatcherServletRegistrationBean . The core logic is to instantiate a
Bean , some parameters are set, such as dispatcherServlet , loadOnStartup etc.
summary
springboot mvc The autoconfiguration class is DispatcherServletAutoConfigration , mainly did two things:
1 )Disposition DispatcherServlet
2 )Disposition DispatcherServlet Registration of Bean(DispatcherServletRegistrationBean)

II Register DispatcherServlet to ServletContext

In the source code browsing in the previous section, we saw DispatcherServlet and DispatcherServletRegistrationBean
these two items. Bean Automatic configuration of. DispatcherServlet We're familiar, DispatcherServletRegistrationBean negative
Responsible general DispatcherServlet Register to ServletContext among

(1) Class diagram of DispatcherServletRegistrationBean

Since the responsibility of this class is to register DispatcherServlet , then we need to know when to trigger the registration operation. To this end,
Let's see first DispatcherServletRegistrationBean Class diagram of this class

(II) process of registering dispatcher Servlet

1. ServletContextInitializer

We see, at the top is a ServletContextInitializer Interface. As we can see, implementing the interface means using
To initialize ServletContext of Let's look at the interface
public interface ServletContextInitializer { 
void onStartup(ServletContext servletContext) throws ServletException; 
}

2. RegistrationBean

See how RegistrationBean implements the onStartup method

@Override
	public final void onStartup(ServletContext servletContext) throws ServletException {
		//Get whether it is a filter, a servlet or a listener
		String description = getDescription();
		if (!isEnabled()) {
			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
			return;
		}
		register(description, servletContext);
	}
Internal call register Method, follow it up

3. DynamicRegistrationBean

Look again DynamicRegistrationBean How register Methodical
@Override
	protected final void register(String description, ServletContext servletContext) {
		D registration = addRegistration(description, servletContext);
		if (registration == null) {
			logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
			return;
		}
		configure(registration);
	}
follow-up addRegistration method
protected abstract D addRegistration(String description, ServletContext servletContext);

4.ServletRegistrationBean

Look again ServletRegistrationBean How addRegistration Methodical
@Override
	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
		String name = getServletName();
		//Be responsible for registering DispatcherServlet in servletContext
		return servletContext.addServlet(name, this.servlet);
	}
We see that here will be directly DispatcherServlet to add here we are servletContext among.

(3) It is embodied in the SpringBoot startup process

getSelfInitializer().onStartup(servletContext);

This code is actually to load SpringMVC So how did he do it? getSelfInitializer() Eventually
To call to ServletWebServerApplicationContext of selfInitialize Method, the method code is as follows

 

private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}
Through debugging, we know getServletContextInitializerBeans() Returns a
ServletContextInitializer Collection, which contains the following objects

 

Then call the object in turn onStartup Method, then for the object with the red icon on it, it will call
DispatcherServletRegistrationBean of onStartup Method, this class does not have this method, so in the end
Will be called to the parent class RegistrationBean of onStartup Method, the method code is as follows

@Override
	public final void onStartup(ServletContext servletContext) throws ServletException {
		//Get whether it is a filter, a servlet or a listener
		String description = getDescription();
		if (!isEnabled()) {
			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
			return;
		}
		register(description, servletContext);
	}
here register(description, servletContext) ; Will call DynamicRegistrationBean of
register Method, the code is as follows:
@Override
	protected final void register(String description, ServletContext servletContext) {
		D registration = addRegistration(description, servletContext);
		if (registration == null) {
			logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
			return;
		}
		configure(registration);
	}
addRegistration(description, servletContext) Will call again ServletRegistrationBean
Medium addRegistration Method, the code is as follows:
@Override
	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
		String name = getServletName();
		//Be responsible for registering DispatcherServlet in servletContext
		return servletContext.addServlet(name, this.servlet);
	}
See the key servletContext.addServlet Code, we can know through debugging this.servlet Just
yes dispatcherServlet

 

summary
SpringBoot automatic assembly SpringMvc It's actually going to ServletContext Added a Dispatcherservlet .
Servlet3.0 The specification has this description, except that it can be added dynamically Servlet, It can also be added dynamically Listener , Filter
  • addServlet
  • addListener
  • addFilter

Topics: Java Spring Boot intellij-idea