SpringBoot嵌入SpringMVC原理分析

承接上文。
在上一篇文章中,我们介绍到了SpringBoot是如何整合Tomcat的。我们知道在SpringBoot项目中可以使用SpringMVC的注解,比如@Controller和@RequestMapping注解。既然我们没有配置SpringMVC那么mvc的功能又是怎么起作用呢?

自动配置(一) 自动配置DispacherServlet和DispacherServletRegistration

首先找到SpringMVC的自动配置类

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

DispatcherServletAutoConfiguration.class

在这里插入图片描述
首先解释一下该类上的注解:根据行号来解释
1.@AutoConfigurationOrder:自动加载时配置该类的顺序
2.@Configuration:表示这是一个配置类
3.@ConditionalOnWebApplication:自动配置该类需要满足当前上下文环境是SERVLET环境
4.@ConditionalOnClass:classpath路径下需要存在DispacherServlet类
5.@AutoConfigureAfter: 在ServletWebServerFactoryAutoConfiguration后配置,这个ServletWebServerFactoryAutoConfiguation就是我们前面文章说的Tomcat的自动配置类

DispatcherServletAutoConfiguration类中有两个内部类分别是:DispatcherServletConfigurationDispatcherServletRegistrationConfiguration

DispacherServletConfiguration.class

@Configuration(proxyBeanMethods = false)
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
	protected static class DispatcherServletConfiguration {

		@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;
		}
		
		// 这个类的主要作用是把用户自己配置的MultipartResolver的名称给配置一下
		// 因为用户自己配置的类名称可能不叫multipartResolver
		// 简单来说就是为了统一MultipartResolver在IOC容器中的bean名称叫 multipartResolver
		@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;
		}

1.@Conditional指明了一个前置条件判断,由DefaultDispatcherServletCondition实现。主要是判
断了是否已经存在DispatcherServlet,如果没有才会触发解析。
2.@ConditionalOnClass指明了当ServletRegistration这个类存在的时候才会触发解析,生成的
DispatcherServlet才能注册到ServletContext中。
3.最后,@EnableConfigrationProperties将会从application.properties这样的配置文件中读取
spring.http和spring.mvc前缀的属性生成配置对象HttpProperties和WebMvcProperties。
由此可见DispatcherServletConfiguration .class 主要作用就是向容器中注册一个DispacherServlet

DispatcherServletRegistrationConfiguration

@Configuration(proxyBeanMethods = false)
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {

		@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;
		}

	}

同样的,@Conditional有一个前置判断,DispatcherServletRegistrationCondition主要判断了该
注册类的Bean是否存在。
@ConditionOnClass也判断了ServletRegistration是否存在
@EnableConfigurationProperties生成了WebMvcProperties的属性对象
@Import导入了DispatcherServletConfiguration,也就是我们上面的配置对象。
内部只有一个方法,生成了DispatcherServletRegistrationBean对象。
DispatcherServletConfiguration是一个注册类,所以DispatcherServletConfiguration的作用就是生成一个DispatcherServletRegistrationBean并向SevletContext中注册DispacherServlet

小结

springboot mvc的自动配置类是DispatcherServletAutoConfigration,主要做了两件事:
1)配置DispatcherServlet
2)配置DispatcherServlet的注册Bean(DispatcherServletRegistrationBean)

自动配置(二)注册DispatcherServlet到ServletContext

上面我们已经看到DispacherServletDispacherServletRegistrationBean注册到了IOC容器中,并在BeanFactoryPostProcessor中进行加载注册。那么现在的疑问是,DispacherServlet是在哪里被注册到ServletContext中的?下面我们就来看DispacherServlet在哪里注册的?

DispacherServletRegistrationBean的类图

在这里插入图片描述DispacherServlet注册流程

根据上一篇文章我们知道注册Tomcat的位置。
现在我们来到**ServletWevServerApplicationContext#createWebServer()**方法中

// 方法1
private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			ServletWebServerFactory factory = getWebServerFactory();
			// 1. getWebServer()的参数有一个getSelfInitializer()
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

进入到**getSelfInitializer()**方法里面

// 方法2
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
}

// 上面的方法又调用了下面的这个方法	
// 方法3
private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
		// 2. 这里调用了getServletContextInitializerBeans(),继续进入
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}

// 继续进入
// 方法4
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
		return new ServletContextInitializerBeans(getBeanFactory());
}

// 方法5
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes) {
		this.initializers = new LinkedMultiValueMap<>();
		this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
				: Collections.singletonList(ServletContextInitializer.class);
		// 3. 点击继续这个方法里面
		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);
	}
	
// 方法6
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
		for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
			for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
					initializerType)) {
				addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
			}
		}
	}

进入方法的调用流程已经标了注释
断点调试放在方法6处:
在这里插入图片描述
可以看到当前initializerBean的类型就是我们上面说自动配置时注册的DispacherServletRegistrationBean
回到方法3处:
他在for循环中调用了onStartUp方法,我们进去看一下。
在这里插入图片描述
根据上面的ServletContextInitializer类图进入RegistrationBean

public final void onStartup(ServletContext servletContext) throws ServletException {
		// 获取当前环境到底是一个filter 还是一个servlet 还是一个 listener
		String description = getDescription();
		if (!isEnabled()) {
			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
			return;
		}
		// 进入 register方法
		register(description, servletContext);
	}

register方法又是一个模板方法,找到他的实现类DynamicRegistrationBean,进入

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),他也是一个模板方法,根据类图关系仅需进入ServletRegistrationBean
在这里插入图片描述
我们看到了servletContext.addServlet(name, this.servlet),这就是真正的将DispacherServlet注册进ServletContext中的方法。this.servlet就是我们获取到的DispacherServlet

小结

SpringBoot自动装配SpringMvc其实就是往ServletContext中加入了一个 Dispatcherservlet 。
Servlet3.0规范中有这个说明,除了可以动态加Servlet,还可以动态加Listener,Filter
addServlet
addListener
addFilter

谢谢!!!!

其他文章
SpringBoot嵌入Tomcat原理分析

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值