SpringMVC源码深度解析(下)

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

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

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

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

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

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

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

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框架中,有些我没讲到的地方还有疑问,欢迎留言,谢谢!

        

相关推荐

  1. SpringMVC分析(八)--参数解析

    2024-07-20 16:08:03       46 阅读
  2. SpringMVC分析(六)--参数名称解析

    2024-07-20 16:08:03       42 阅读
  3. SpringMVC分析(九)--返回值解析

    2024-07-20 16:08:03       38 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 16:08:03       106 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 16:08:03       116 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 16:08:03       95 阅读
  4. Python语言-面向对象

    2024-07-20 16:08:03       103 阅读

热门阅读

  1. css样式

    css样式

    2024-07-20 16:08:03      27 阅读
  2. deque学习笔记

    2024-07-20 16:08:03       27 阅读
  3. 题解:T480715 true

    2024-07-20 16:08:03       29 阅读
  4. 你有多自律就有多自由

    2024-07-20 16:08:03       28 阅读
  5. 2024 暑假友谊赛 2

    2024-07-20 16:08:03       33 阅读
  6. 【CTFWP】ctfshow——web41

    2024-07-20 16:08:03       28 阅读
  7. Scala学习笔记19: 隐式转换和隐式参数

    2024-07-20 16:08:03       31 阅读
  8. Qmi8658a姿态传感器使用心得(2)linux

    2024-07-20 16:08:03       28 阅读
  9. springcloud与dubbo的rpc通信都是分别基于什么实现的

    2024-07-20 16:08:03       24 阅读
  10. AI论文写作软件哪些比较好用?

    2024-07-20 16:08:03       28 阅读
  11. vue-treeselect

    2024-07-20 16:08:03       27 阅读
  12. 反悔贪心

    2024-07-20 16:08:03       27 阅读
  13. 我们的耳穴项目迈进了一大步

    2024-07-20 16:08:03       28 阅读
  14. 【前后端联调】HttpMessageNotReadableException

    2024-07-20 16:08:03       23 阅读