【SpringMVC源码】容器创建,请求处理,异常处理,视图渲染等系列流程

我们熟知的流程

在这里插入图片描述
①②:前端控制器DispatcherServlet(所有的请求都有经过它来统一分发)拦截请求,交由HandlerMapping返回HandlerExecutionChain【包括包含一个Handler处理器对象、多个HandlerInterceptor拦截器】,

HandlerMapping完成了客户请求到Controller之间的映射,只不过到control还需要一些步骤如③④包装一下(加一层花样多一些嘛),有下面几个
BeanNameUrlHandlerMapping
SimpleUrlHandlerMapping
ControllerClassNameHandlerMapping

③④:HandlerExecutionChain包装为适配器(支持多种控制器),然后返回ModelAndView对象【包含了模型(Model)和视图(View)】

:如果视图(View)本身是

  • View对象
  • 只是逻辑名,还需要经过视图解析器变成View对象

:View根据模型渲染页面

与spring的关系

在这里插入图片描述

简单来说就是父子容器的关系,只是每个有些设置不同,关于容器创建可以去看看spring源码,其中最为特殊的就是springMVC会注册ContextListner,之后再finishRefresh中会触发九大组件的完成

multicastEvent 遍历所有的listner ,其中ContextRefreshLisener处理方法会注册设置九大组件

上面就是两个容器的创建,之后就是service方法来接受请求了,其中最重要的就是doDispatch,依靠九大组件来完成请求的处理

重点doDispatch讲解

下面是doDispatch的线型代码流程,我将把每个小框框的作用讲出,串出来就是处理流程
在这里插入图片描述

1. 检验是否为文件上传请求

在这里插入图片描述

主要根据contentType(是否是 mutipart/ 开头)来判断处理,解析出上传的文件

processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

2. HandlerExecutionChain 的寻找

根据HandlerMapping来寻找对应的

  • handler(handler就是我们写得处理这个请求逻辑)
  • interceptors 拦截器
HandlerExecutionChain mappedHandler = null;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
	noHandlerFound(processedRequest, response);
	return;
}

2.1 handler的寻找

handler的寻找我们需要依靠handlerMapping这个组件
其中有主要的两个HandlerMapping实现,分别是AbstractHandlerMethodMapping,AbstractUrlHandlerMapping,类图如下

在这里插入图片描述

了解这个两个类之前需要了解,Controller的3种实现方式,可以参考这个

  • 标注@Controller注解,注解指定url
  • 继承Controller类,实现handleRequest方法,XML里面指定url
  • 继承HttpRequestHandler,实现handleRequest方法,XML指定url
  • AbstractUrlHandlerMapping解决有@Controller方式的handler寻找

如何寻找?AbstractUrlHandlerMapping内部维护了MappingRegistry内部有六个集合,主要用的是mappingLookup,保存了从url到方法的映射。
在这里插入图片描述
mappingLookup在哪初始化?因为AbstractUrlHandlerMapping继承了InitializingBean这个MappingRegistry集合是在容器创建这个bean时的setProperties方法设置好的

  • AbstractHandlerMethodMapping类解决继承Controller和HTTPRequestHandler的Controller

如何寻找?AbstractHandlerMethodMapping内部保存了,Map<PathPattern, Object> handlerMap,保存了从url到controller对象的映射
handlerMap在哪初始化?AbstractHandlerMethodMapping继承了ApplicationContextAware就会在容器创建这个bean时执行invokeAwareMethod

2.2 interceptors的寻找

主要根据handler的url来在adaptedInterceptors匹配,能匹配上的就在chain的interceptor集合中加入。

adaptedInterceptors在哪初始化?同样继承了ApplicationContextAware就会在容器创建这个bean时执行invokeAwareMethod进行初始化

3.获取handler对应handlerAdapter

在这里插入图片描述
上述寻找handler的过程,其中每个handler方法名字都不一样,所以加一层适配,使得可以统一调用handle方法,当然adapter远远不止这么简单,因为后面调用就是靠这个类,所以这个类需要具备一个功能,这也是其中最繁琐的实现,参数值的处理解析

4.处理last modified

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

这个方法可以帮助我们减少获取资源的次数

5. intercetpor的prehandler方法

这里就是我们经常在项目里写的拦截器的prehandler方法执行的地方

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

6. handler对应handlerAdapter调用

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

handler的adapter有很多,下面就拿我们经常使用的RequestMappingHandlerAdapter讲

RequestMappingHandlerAdapter主要处理对于@Controller标注的类,我分为创建和调用,需要注意的是创建实在springMVC容器启动时创建的,这个doDispatch进行了调用,但必须要讲解创建过程才能理解

6.1 创建

对于容器对RequestMappingHandlerAdapter对象的创建,最重要的在于afterPorpertiesSet方法,对下图属性进行了设置
在这里插入图片描述

  • initBinderAdviceCache:设置全局配置的InitBinder,全局类型的InitBinder需要声明的类上使用@ControllerAdvice进行标注
  • modelAttributeAdviceCache::设置全局配置的ModelAttribute,全局类型的ModelAttribute需要声明的类上使用@ControllerAdvice进行标注
  • argumentResolvers:用于根据当前handler的方法参数标注的注解类型,如@RequestParam,@ModelAttribute等,获取其对应的ArgumentResolver,以将request中的参数转换为当前方法中对应注解的类型
  • returnValueHandlers:通过ReturnValueHandler对返回值进行适配

@InitBinder可以绑定请求参数到指定的属性编辑器,比如

  • 把传过来的string进行trim修剪
  • 把传来string类型的日期转java Date类型
    在这里插入图片描述

6.2 调用

调用也有准备过程和调用过程
准备主要准备:initBinder,modelAttribute,参数处理器,返回值处理器等等

在这里插入图片描述

6.2.1 准备

在这里插入图片描述

可以看出来之前创建RequestMappingHandlerAdapter的设置的值都用上了,但是多了个两个 initBinderCache,modelAttributeCache,这是局部的@InitBinderCache,@ModelAttribute,只在定义的那个类中生效,而不是全局的。这样我们就需要将全局和局部的都设置到此次调用过程准备材料中。获取所需InitBinder与ModelAttribute代码过程十分类型(先获取局部再全局最后一起缓存),获取所需InitBinder如下

Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
	methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
	this.initBinderCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
	if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
		Object bean = controllerAdviceBean.resolveBean();
		for (Method method : methodSet) {
			initBinderMethods.add(createInitBinderMethod(bean, method));
		}
	}
});
for (Method method : methods) {
	Object bean = handlerMethod.getBean();
	initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);

先是在局部initBinderCache获取,如果没有再去selectMethods,找到局部initBinder;之后再获取全局。之后全局与局部一起封装到initBinderMethods返回

6.2.2 调用

在这里插入图片描述

//获取值reslover
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
	HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
	if (result == null) {
		for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
			if (resolver.supportsParameter(parameter)) {
				result = resolver;
				this.argumentResolverCache.put(parameter, result);
				break;
			}
		}
	}
	return result;
}
  • 重要的返回值处理器介绍
    我们标注@ResponseBody就会返回json,那么这里就是returnValueHanlders的作用,这些handler都实现了supportsReturnType和handleReturnType两个方法
    其中处理@ResponseBody的returnValueHanlders 为RequestResponseBodyMethodProcessor类,其处理流程如下
    在这里插入图片描述

7. 应用默认视图名

applyDefaultViewName(processedRequest, mv);
/**
 * Do we need view name translation?
 */
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
	if (mv != null && !mv.hasView()) {
		String defaultViewName = getDefaultViewName(request);
		if (defaultViewName != null) {
			mv.setViewName(defaultViewName);
		}
	}
}

8. interceptor的postHandler执行

mappedHandler.applyPostHandle(processedRequest, response, mv);

9. 异常处理

写在processHandlerException内部,如果再前面的步骤发生异常,catch到,那么在这里就会检测到,来进行处理

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

在这里插入图片描述
异常处理并不复杂,复杂的是异常处理也是个方法,也要涉及参数和返回值的处理,与之前adapter执行handler方法十分类似,如下图
在这里插入图片描述

10. 页面渲染

这里我们要十分主要的是,会根据我们是否有mv,如果有mv就会进入视图渲染,否则返回原始数据。

// 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.");
	}
}

这里也很好理解,那如何会返回原始数据呢,我就拿@ResponseBody的returnValueHanlders 为RequestResponseBodyMethodProcessor类说下

再进行处理时进入的入口有很关键的一条语句,这里表明不需要mv

mavContainer.setRequestHandled(true);

之后再adapter的handle的最后一个getModelAndView方法执行如下:如果mavContainer.isRequestHandled() 为ture ,那么返回null,返回null就会导致mv为空,这样不会经过渲染而直接返回视图

modelFactory.updateModel(webReqest, mavContainer);
if (mavContainer.isRequestHandled()) {
	return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
	mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
	Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
	HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
	if (request != null) {
		RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
	}
}
return mav;

11. 执行interceptor的triggerAfterCompletion

if (mappedHandler != null) {
	mappedHandler.triggerAfterCompletion(request, response, null);
}

源码细节太多,后续不断补充

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yilyil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值