SpringMVC源码深度解析(下)

接着上一遍博客《SpringMVC源码深度解析(中)》继续聊。上一篇博客中,返回的是对象的情况下SpringMVC框架会怎么处理,这种情况也是现在用得最多的,因为都是前后端分离。如果返回的是ModelAndView,则是另外的处理逻辑了,主要看ModelAndViewMethodReturnValueHandler对象,代码如下:

到这里位置,视图的解析渲染就讲完了,回到DispatcherServlet#processDispatchResult()方法,代码如下:

该注解可以添加到类/方法上,最终会注解中设置的异常原因和状态码设置到响应中。回到DispatcherServlet#processDispatchResult()方法,再看看这里:

再回到DispatcherServlet#processDispatchResult()方法被调用的地方。 由上面的代码可知:如果有异常,但是没有配置异常处理解析器或者异常解析处理器返回的视图为空的话,异常会继续往外抛,由于最外层还有一个try/catch,最终会调用DispatcherServlet#triggerAfterCompletion()方法,代码如下:

到这里为止,DispatcherServlet的流程大概讲完了。当然地方也没有讲到,有兴趣的朋友可以自行研究,起码我觉得了解到这个程度,在日常开发中应该是完全够用了。接下来我会讲一下流程中没有讲到的东西,比较零散,讲到哪里是哪里,望理解。

第一个想到的就是 @EnableWebMvc注解,它的作用是什么呢?先看看这个注解:

看到了@Import注解,马上就能想到,这是Spring框架的扩展点之一,该注解中的类会被放入Spring容器中。看看该类,代码如下:

java 复制代码
package org.springframework.web.servlet.config.annotation;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;

/**
 * A subclass of {@code WebMvcConfigurationSupport} that detects and delegates
 * to all beans of type {@link WebMvcConfigurer} allowing them to customize the
 * configuration provided by {@code WebMvcConfigurationSupport}. This is the
 * class actually imported by {@link EnableWebMvc @EnableWebMvc}.
 *
 * @author Rossen Stoyanchev
 * @since 3.1
 */
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}


	@Override
	protected void configurePathMatch(PathMatchConfigurer configurer) {
		this.configurers.configurePathMatch(configurer);
	}

	@Override
	protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		this.configurers.configureContentNegotiation(configurer);
	}

	@Override
	protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
		this.configurers.configureAsyncSupport(configurer);
	}

	@Override
	protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		this.configurers.configureDefaultServletHandling(configurer);
	}

	@Override
	protected void addFormatters(FormatterRegistry registry) {
		this.configurers.addFormatters(registry);
	}

	@Override
	protected void addInterceptors(InterceptorRegistry registry) {
		this.configurers.addInterceptors(registry);
	}

	@Override
	protected void addResourceHandlers(ResourceHandlerRegistry registry) {
		this.configurers.addResourceHandlers(registry);
	}

	@Override
	protected void addCorsMappings(CorsRegistry registry) {
		this.configurers.addCorsMappings(registry);
	}

	@Override
	protected void addViewControllers(ViewControllerRegistry registry) {
		this.configurers.addViewControllers(registry);
	}

	@Override
	protected void configureViewResolvers(ViewResolverRegistry registry) {
		this.configurers.configureViewResolvers(registry);
	}

	@Override
	protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
		this.configurers.addArgumentResolvers(argumentResolvers);
	}

	@Override
	protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
		this.configurers.addReturnValueHandlers(returnValueHandlers);
	}

	@Override
	protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		this.configurers.configureMessageConverters(converters);
	}

	@Override
	protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		this.configurers.extendMessageConverters(converters);
	}

	@Override
	protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
	}

	@Override
	protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		this.configurers.extendHandlerExceptionResolvers(exceptionResolvers);
	}

	@Override
	@Nullable
	protected Validator getValidator() {
		return this.configurers.getValidator();
	}

	@Override
	@Nullable
	protected MessageCodesResolver getMessageCodesResolver() {
		return this.configurers.getMessageCodesResolver();
	}

}

有很多实现方法,逻辑类似,如DelegatingWebMvcConfiguration#addArgumentResolvers()方法为例:

看到上面的这段代码就可以知道:从Spring容器中获取到的WebMvcConfigurer对象的集合,并调用DelegatingWebMvcConfiguration#addArgumentResolvers(),即遍历WebMvcConfigurer对象的集合,将设置的HandlerMethodArgumentResolver的集合设置给WebMvcConfigurer对象。

当然,除了设置HandlerMethodArgumentResolver外,还可以设置HttpMessageConverter、HandlerMethodReturnValueHandler、InterceptorRegistry、CorsRegistry、HandlerExceptionResolver 等等。由于DelegatingWebMvcConfiguration类还是被@Configuration注解所修饰,因此它还是个配置类,配置相关都在它的父类中,即WebMvcConfigurationSupport,看看这个类,截取部分代码如下:

这是添加拦截器的逻辑,因此可以知道,除了添加@EnableWebMvc注解外,还需要定义一个WebMvcConfigurer接口的实现了,并将其注册到Spring容器中。以添加拦截器为例:实现了WebMvcConfigurer接口,需要实现WebMvcConfigurer#addInterceptors()方法,入参为InterceptorRegistry,将自定义的拦截器添加到传入的 InterceptorRegistry对象中。在DelegatingWebMvcConfiguration类中,会获取到Spring容器中WebMvcConfigurer对象的集合。在WebMvcConfigurationSupport中,比如创建HandlerMapping对象的时候,就会调用到WebMvcConfigurationSupport#getInterceptors()方法,进而调用到DelegatingWebMvcConfiguration#addInterceptors()方法,传入InterceptorRegistry对象,实际上就是进行拦截器的设置,最终会将自定义的拦截器设置到InterceptorRegistry对象中。

如果是设置其他的对象,如HandlerMethodArgumentResolver、HttpMessageConverter、CorsRegistry、HandlerMethodReturnValueHandler,是同样的原理。对于HttpMessageConverter而言,有点不同,看看代码:

到这里为止,@EnableWebMvc注解的原理讲完了。再看看@ControllerAdvice注解,这是示例,代码如下:

一般@ControllerAdvice和@ExceptionHandler注解是配合使用的,想知道原理,线看看注解,代码如下:

对于这个注解是怎么生效的,还是需要先回到WebMvcConfigurationSupport中,由于是跟异常相关,因此肯有可能看HandlerExceptionResolver类,看看WebMvcConfigurationSupport中哪里有配置HandlerExceptionResolver类,搜的时候也确实搜到了,代码如下:

看看 ExceptionHandlerExceptionResolver类,同样只截取核心的代码:

发现ExceptionHandlerExceptionResolver实现了InitializingBean,当然需要看看ExceptionHandlerExceptionResolver#afterPropertiesSet()方法,代码如下:

通过ControllerAdviceBean#findAnnotatedBeans()方法,可以获取ControllerAdviceBean对象的集合(被@ControllerAdvice修饰的Class被封装成该类),遍历,获取到beanType,也就是被@ControllerAdvice修饰的Class,创建ExceptionHandlerMethodResolver对象,调用它的有参构造,传入beanType,看看ExceptionHandlerMethodResolver类,代码如下:

到这里为止,ExceptionHandlerExceptionResolver的exceptionHandlerAdviceCache属性,已经不为空了,缓存的是ExceptionHandlerMethodResolver对象,而ExceptionHandlerMethodResolver对象的mappedMethods属性,缓存的才是我们想要的,key为异常类型,value为被@ExceptionHandler注解修饰的方法。

再想想会在哪里使用呢?由于是报错,前面我讲到的DispatcherServlet流程中,一定会进到某个方法,先看看那块的代码:

可知,前面通过try/catch捕获到异常,并调用DispatcherServlet#processDispatchResult()方法进行处理的时候,传入异常对象dispatchException,再点进这个方法看看:

看看ExceptionHandlerExceptionResolver#getExceptionHandlerMethod()方法,代码如下:

再看看ServletInvocableHandlerMethod#invokeAndHandle()方法,代码如下:

到这里为止,@ControllerAdvice和@ExceptionHandler注解的原理讲完了。

SpringMVC框架,到这里也算是讲完了,可能有一些遗漏。如果对SpringMVC框架中,有些我没讲到的地方还有疑问,欢迎留言,谢谢!

相关推荐
半盏茶香44 分钟前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Evand J2 小时前
LOS/NLOS环境建模与三维TOA定位,MATLAB仿真程序,可自定义锚点数量和轨迹点长度
开发语言·matlab
LucianaiB2 小时前
探索CSDN博客数据:使用Python爬虫技术
开发语言·爬虫·python
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
计算机学长大白3 小时前
C中设计不允许继承的类的实现方法是什么?
c语言·开发语言
suweijie7683 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿4 小时前
List深拷贝后,数据还是被串改
java
PieroPc4 小时前
Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印
开发语言·python·excel
hackeroink5 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss