Spring MVC源码详解

什么是Spring MVC ?

Spring MVC就是Spring+MVC。

Spring就不介绍了,什么是MVC?

  • M:模型,javabean
  • V:视图,如jsp
  • C:控制层,如Controller、Servlet

SpringMVC请求处理流程

  1. 用户发送请求至前端控制器DispatcherServlet;
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器;
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)HandlerExecutionChain一并返回给DispatcherServlet;
  4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器,执行处理器(Controller,也叫后端控制器);
  5. Controller执行完成返回ModelAndView,并返回给HandlerAdapter,HandlerAdapter将结果返回给DispatcherServlet;
  6. DispatcherServlet将ModelAndView传给ViewReslover视图解析器,ViewReslover解析后返回具体View给DispatcherServlet;
  7. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)后返回给给客户;

源码分析

代码调试准备

新建项目

新建Springboot项目,依赖如下:

java 复制代码
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

打断点

断点打在下面的位置:

org.springframework.web.servlet.DispatcherServlet#initStrategies:初始化阶段

org.springframework.web.servlet.DispatcherServlet#doDispatch:运行调用阶段。

项目调试启动时未进入上面的两个方法,当第一次调用Controller的请求时,会执行初始化阶段代码,每次调用Controller都会调用运行调用阶段代码。

初始化阶段

initStrategies方法初始化

第一次调用Controller时,会调用org.springframework.web.servlet.DispatcherServlet#initStrategies方法初始化SpringMVC的九大组件

java 复制代码
protected void initStrategies(ApplicationContext context) {
		//多文件上传组件
		initMultipartResolver(context);
		//初始化本地语言环境
		initLocaleResolver(context);
		//初始化模版处理器
		initThemeResolver(context);
		//初始化处理器映射器
		initHandlerMappings(context);
		//初始化处理器适配器
		initHandlerAdapters(context);
		//初始化异常拦截器
		initHandlerExceptionResolvers(context);
		//初始化视图预处理器
		initRequestToViewNameTranslator(context);
		//初始化视图解析器
		initViewResolvers(context);
		//初始化FlashMap管理器
		initFlashMapManager(context);
	}

org.springframework.web.servlet.DispatcherServlet#initStrategies方法的调用链路如下:

HandlerMapping初始化

HandlerMapping是处理器映射器,简单讲是Controller中的方法与请求地址的映射关系,通过它可以找到Controller中的方法来处理请求。

什么是Handler?

Handler是controller中带请求路径的方法,最常用的是Controller中@RequestMapping注解标注的方法。如下面getProduct方法是Handler,loginPage方法不是Handler。

java 复制代码
@Controller
@Slf4j
public class ClassifyController {
    @Autowired
    IClassifyService classifyService;




    @RequestMapping("/getClassify")
    @ResponseBody
    Object getProduct(){
        log.error("你妹的");
       return classifyService.selectByTenant("0000000001");
    }
//    @GetMapping("/index")
    public String loginPage(Model model){
        model.addAttribute("name","登科");
        return "welcom";
    }

}

Spring中共四种Handler

  • 加RequestMapping注解的方法(常用的)
  • 实现Controller接口的Bean对象(古老的方法)
  • 实现HttpRequestHandler接口的Bean对象(古老的方法)
  • HandlerFunction对象(新的)

什么是HandlerMapping?

HandlerMapping是处理器映射器,简单讲是Controller中的方法与请求地址的映射关系,通过它可以找到Controller中的方法来处理请求。

三种常用的HandlerMapping

  • BeanNameUrlHandlerMapping:处理实现Controller、HttpRequestHandler接口的Bean对象对应的Handler
  • RequestMappingHandlerMapping:处理加RequestMapping注解的方法对应的Handler,也是最常用的。
  • RouterFunctionMapping:处理HandlerFunction对象对应的Handler

HandlerMapping初始化源码分析

java 复制代码
private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			//获取所有的HandlerMappings,其中包括RequestMappingHandlerMapping
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				//将获取到HandlerMapping赋值给DispatcherServlet.handlerMappings 
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

下面的代码获取了所有的HandlerMapping,其中包括RequestMappingHandlerMapping

java 复制代码
Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

以RequestMappingHandlerMapping为例,HandlerMapping中是如何为何请求地址与Handler之间的映射呢?

上面的方法通过BeanFactory获取RequestMappingHandlerMapping对应的实例,因为RequestMappingHandlerMapping是单例的,容器启动的时候会创建单例对象。查看RequestMappingHandlerMapping的父类,实现了InitializingBean接口,因此创建实例的时候会执行初始化方法,而绑定请求地址与Handler的逻辑就在初始化方法中。

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet

java 复制代码
public void afterPropertiesSet() {
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(useSuffixPatternMatch());
		this.config.setTrailingSlashMatch(useTrailingSlashMatch());
		this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
		this.config.setContentNegotiationManager(getContentNegotiationManager());
		//跳转到父类的初始化方法
		super.afterPropertiesSet();
	}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet

java 复制代码
@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods

java 复制代码
protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				//绑定url与Handler的逻辑
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean

java 复制代码
protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			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);
			}
		}
		//isHandler方法就是判断这个bean是否是Controller(有@Controller或者@RequestMapping注解)
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods

根据给定的Controller,找出这个Controller中的所有Handler,也就是带@RequestMapping注解的方法

java 复制代码
protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

最后通过org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod方法将映射关系维护到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#registry对应的Map中。key是mapping对象,其实就是@RequestMapping注解中对应的信息,value是MappingRegistration对象。

java 复制代码
		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

MappingRegistration中

  • mapping是@RequestMapping注解中对应的信息

  • handlerMethod是Handler,也就是@RequestMapping注解的方法

  • directUrl是请求地址,这里观察是/getClassify

  • mappingName是映射的名称,这里观察室CC#getProduct

java 复制代码
private static class MappingRegistration<T> {

		private final T mapping;

		private final HandlerMethod handlerMethod;

		private final List<String> directUrls;

		@Nullable
		private final String mappingName;
		............
}

HandlerMapping初始化总结

  • org.springframework.web.servlet.DispatcherServlet#initStrategies:初始化SpringMVC的九大组件。第一次调用请求Controller时,会初始化DispatcherServlet,因此会调用该方法
  • org.springframework.web.servlet.DispatcherServlet#initHandlerMappings:初始化处理器映射器
  • 获取所有的处理器映射器实例,并放入到org.springframework.web.servlet.DispatcherServlet#handlerMappings中,其实是List。以RequestMappingHandlerMapping这个处理器映射器为例子,因为RequestMappingHandlerMapping是单例、非延时加载,因此启动项目时会实例化该对象,handlerMappings是直接从缓存中获取的对象。
  • 实例化RequestMappingHandlerMapping对象时会调用初始化方法org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
  • 初始化方法会调用父类初始化方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
  • 父类初始化方法中会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
  • initHandlerMethods方法中会调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean
  • processCandidateBean方法中,会判断是否为Controller,如果是则调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods,该方法会查找Controller中所有的Handler,也就是该Controller所有标注@RequestMapping注解的方法,并解析注册到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#registry中 。
  • 最终请求相关的信息和Handler就会绑定在一起,存储在HandlerMapping中。

运行调用阶段

随便调用一个Controller中的请求,会访问到org.springframework.web.servlet.DispatcherServlet#doDispatch方法中,调用链路如下:

java 复制代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				//判断是否为文件上传请求
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				//根据请求获取handler,获取到的是HandlerExecutionChain,HandlerExecutionChain中包括handler和interceptors
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					//走没有Handler的逻辑,404
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.		
				//获取处理器适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				//调用handler,也就是Controller中对应的@RequestMapping注解的方法,并返回ModelAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				//结果视图对象处理
				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

根据请求获取Handler

下面的代码是根据请求获取handler,获取到的是HandlerExecutionChain,HandlerExecutionChain中包括handler和interceptors。

java 复制代码
mappedHandler = getHandler(processedRequest);

org.springframework.web.servlet.DispatcherServlet#getHandler方法,遍历所有的handlerMapping,其中包括RequestMappingHandlerMapping,根据Request通过HandlerMapping获取Handler。

java 复制代码
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler方法,根据请求获取HandlerExecutionChain。HandlerExecutionChain中包含handler和interceptors。因为初始化阶段初始化HandlerMapping时,已经将请求信息与Handler绑定到一起,所以这里可以通过绑定信息获取,不继续往下跟了。

根据请求获取HandlerAdapter

java 复制代码
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

之前初始化阶段也初始化了HandlerAdapter,因此有下面的几个:

org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter方法中,循环遍历HandlerAdapter,匹配到则返回,这里匹配到了RequestMappingHandlerAdapter。

java 复制代码
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {//初始化阶段获取的实例
			for (HandlerAdapter adapter : this.handlerAdapters) {//遍历所有的HandlerAdapter ,匹配到则返回
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

调用通过HandlerAdapter调用handler

这里执行了Controller中@RequestMapping注解的方法,走到了请求真正想处理的逻辑,并返回ModelAndView

java 复制代码
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle方法:

java 复制代码
@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return handleInternal(request, response, (HandlerMethod) handler);
	}

调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal方法

调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod

上面的方法中通过下面代码,最终通过反射调用方法。

java 复制代码
invocableMethod.invokeAndHandle(webRequest, mavContainer);

视图解析器返回View,并渲染页面

org.springframework.web.servlet.DispatcherServlet#processDispatchResult

java 复制代码
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {//如果有异常则渲染异常页面
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			//获取视图并进行渲染
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

获取视图并进行页面渲染

java 复制代码
if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}

org.springframework.web.servlet.DispatcherServlet#render

方法中

java 复制代码
//根据视图解析器获取视图,这里的视图解析器是初始化的时候通过initViewResolvers方法获取的
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
java 复制代码
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception {

		if (this.viewResolvers != null) {
			for (ViewResolver viewResolver : this.viewResolvers) {
				View view = viewResolver.resolveViewName(viewName, locale);
				if (view != null) {
					return view;
				}
			}
		}
		return null;
	}

org.springframework.web.servlet.DispatcherServlet#render方法中的下面代码,对视图进行渲染。

java 复制代码
view.render(mv.getModelInternal(), request, response);

SpringMVC运行调用阶段源码总结

  • org.springframework.web.servlet.DispatcherServlet#getHandler:根据请求通过HandlerMapping获取Handler
  • org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter:根据Handler获取HandlerAdapter
  • org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle:调用HandlerAdap的handle方法,执行Handler(Controller中的方法)中的逻辑,返回ModelAndView。
  • org.springframework.web.servlet.DispatcherServlet#processDispatchResult:处理视图相关逻辑
  • org.springframework.web.servlet.DispatcherServlet#resolveViewName:根据视图解析器获取视图View
  • 调用View的render方法渲染jsp页面
相关推荐
xlsw_3 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹3 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫4 小时前
泛型(2)
java
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石4 小时前
12/21java基础
java
李小白665 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp5 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶5 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb