Spring源码分析-06-SpringMVC- 处理request请求
文章简介:
本文通过一条request请求(/login) 详细描述了 SpringMVC 处理request,相应response的流程.
本文通过图片,文字描述,SpringMVC源码的方式来描述该过程,希望读者能够有所收获.
**文章可能存在语言描述问题,如出现此类错误,还希望读者可以指出错误,以帮助作者完善文章. **
- PS: 若文章字体偏大或者偏小,建议通过 ctrl键+鼠标滑轮 进行修改,以提升阅读效果.(带来不便,请谅解!)
Version:
- jdk 1.8
- Spring Boot: 2.3.4
- 参考代码 https://gitee.com/leifengyang/springboot2.git
测试案例:
案例: 用户登录
@Controller
public class UserController{
@PostMapping("/login")
public String main(User user, HttpSession session, Model model){ //RedirectAttributes
if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
//把登陆成功的用户保存起来
session.setAttribute("loginUser",user);
//登录成功重定向到main.html; 重定向防止表单重复提交
return "redirect:/main.html";
}else {
model.addAttribute("msg","账号密码错误");
//回到登录页面
return "login";
}
}
}
案例结果:
页面重定向到 localhost:8080/main.html
注意: 源码分析流程 以此为主.
人话描述流程:
客户端发送请求到SpringMVC相应请求时序图:
本图较为仔细的给出了 request 的跳转流程. 仅做认知即可,不需要特别关注该过程.
堆栈信息图:
总体流程:
即DispatcherServlet 调用doDispatch() 的过程.
获取 webAsyncManager ,处理异步请求 的一个东西.(本文章内不考虑此过程)
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
处理 request 请求
处理文件上传请求 即 processedRequest = checkMultipart(request);
获取 HandlerExecutionChain处理器执行链 即 mappedhandler(包含拦截器链Interceptors)
- mappedHandler = getHandler(processedRequest);//mappedHandler 与XXXController 相关联
为当前 request 请求 寻找Adapter (对于整体流程而言不重要)
处理 get请求的header 方法(跳过,不重要)
正序 处理mappedHandler(HandlerExecutionchain) 中拦截器链的preHandle方法.
- mappedHandler.applyPreHandle(processedRequest, response)
真正处理request请求方法(重要!!!)
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
倒序 处理 拦截器链的postHandle() 方法
- mappedHandler.applyPostHandle(processedRequest, response, mv);
处理返回结果 modelAndView 即mv , 并渲染页面(重要)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
补充: 步骤2 和步骤3 通过try catch 方式运行, catch 异常后, 倒序遍历 拦截器链Interceptors , 执行aftercompletion()方法
主要流程:
拦截器链的调用流程:
图片来自 网络 地址:here
try{
//1. 正序 执行preHandle
mappedHandler.applyPreHandle(processedRequest, response)
//2. 真正处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 3, 倒序 执行 postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}catch (Exception e){// 倒序执行 afterCompletion() 方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
由于本部分内容源码过于简单, 不再赘述.
真正处理request请求的流程:
即 invokeHandlerMethod() 方法流程.
PS 省略掉部分内容(包含HTTPSession判断, 本文只关心主线)
设置属性 (不重要)
- 设置 请求参数处理器 即 argumentResolvers
- 设置 结果返回值处理器, 即returnValueHandlers
- 设置其他属性值…..
执行目标方法 即 invocableMethod.invokeAndHandle(webRequest, mavContainer);
- 执行 request 请求, 即执行 XXXController 对应方法(不过需要先处理方法参数) 即 invokeForRequest() 重要!!!
- 处理方法参数 即getMethodArgumentValues(); //重要
- 执行目标方法 doInvoke() . 即通过反射调用XXXController 与之对应的方法.
- 处理返回值 放入mavContainer中. 即this.returnValueHandlers.handleReturnValue()
- 执行 request 请求, 即执行 XXXController 对应方法(不过需要先处理方法参数) 即 invokeForRequest() 重要!!!
根据mavContainer 和 request生成modelAndView
即 getModelAndView(mavContainer, modelFactory, webRequest);
补充说明: args 参数处理流程 和 返回值处理流程大致相似, 下面以参数处理流程进行描述.
args方法参数处理流程:
获取方法参数
MethodParameter[] parameters = getMethodParameters();
依次遍历paramters, 对每个方法参数parameter 调用与其适配的argResolver进行参数处理.
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
遍历 this.argumentResolvers ,寻找 可以处理方法参数 parameter的HandlerMethodArgumentResolver
调用 handlerMethodArgumentResolver.resolveArgument() 方法 处理当前参数
PS: 源码分析过程中给出 结果返回值处理过程(找resolver, 然后执行handleReturnValue() 方法
处理放回结果mv ,渲染页面流程:
即processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
判断是否有exception ,有的话生成errorView ,并返回
render(mv,request,response) 渲染页面
设置response.locale
根据viewName 生成view 对象(页面)
渲染生成的view页面(这里以RedirectView页面为例)
生成model 即 Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
将model与view绑定 以RedirectView为例
即 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
- 生成 url
- 调用response.sendRedirect(url);
倒序执行拦截器链 **afterCompletion()**方法
补充说明:
参数处理器类图
返回值处理器类图
源码分析:
总流程:
- DispatcherServlet.class
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//1. 获取 异步请求 管理器 webAsyncManager
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
// 2. 处理请求
try {// 2.1 检查是否是文件上传,如果文件需要上传,返回 可以处理文件传输的request MultipartHttpServletRequest
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//2.2 获取 HandlerExecutionChain 处理器执行链( 包含拦截器列表) (mappedHandler 与 XXXController.class相关联)
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2.3 为当前 request 请求 寻找Adapter
// 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)) {// 处理 request head 请求
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//2.4 正序 处理拦截器链中每个 Interceptor 的 前置方法 preHandle(), 若返回为false ,倒序执行
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return; // 如果被拦截, 直接返回, 不对请求进行处理
}
// 2.5 真正处理请求 的方法 . 生成 modelAndView
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//2.6 倒序 处理拦截器链中每个Interceptor 的 后置方法, postHandle()
applyDefaultViewName(processedRequest, mv); //设置viewName
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);
}// 2.7 处理 返回结果 并 渲染页面. 从定向,response
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {// 捕获异常,并 倒序遍历Interceptor list 执行 aftercompletion() method
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);
}
}
}
}
拦截器链调用:
由于本部分内容源码过于简单, 不再赘述.
真正处理request请求:
即 RequestMappingHandlerAdapter.invokHandlerMethod()
RequestMappingHandlerAdapter.class
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 生成 servletwebRequest
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try { //1 . 参数设置
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {//1.1设置请求参数处理器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {//1.2设置结果返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 省略掉部分代码. 设置webAsyncManager
// 2. 方法执行
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 3.根据 mavContainer ,webRequest 生成 modelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
参数设置:
1. 设置参数处理器
2. 设置返回值处理器
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//....
if (this.argumentResolvers != null) {//1.1设置请求参数处理器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {//1.2设置结果返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
/// .....
}
执行目标方法
即 invocableMethod.invokeAndHandle(webRequest, mavContainer);
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//1, 执行 request 请求, 即执行 XXXController 对应方法(不过需要先处理方法参数)
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);// 设置response status
// 处理返回值为空的情况
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
//2 . 将返回值 与mav 绑定
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {// 就是这个方法
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
执行request请求:
- InvocableHandlerMethod.class
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//1 . 处理方法请求参数 , argmentResolver
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}//2 .执行 方法 XXXController 与之对应的方法
return doInvoke(args);
}
返回值放入mvContainer中
- HandlerMethodReturnValueHandlerComposite.class
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 遍历this.returnValueHandlers 寻找合适的 returnvalueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}// 处理 returnvalue (原理, 单一接口继承原则, 不同借口,不同方法)
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
以ViewNameMethodReturnValueHandler.class为例
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 判断returnValue 是否是字符串
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
生成ModelAndView
- RequestMappingHandlerAdapter.class
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, 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;
}
处理放回结果mv ,渲染页面:
- DispatcherServlet.class
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
// 1. 判断是否出现Exception 生成errorView
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);
}
}
// 2. 根据modelAndView 渲染页面
// 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;
}
//3. 倒序执行 拦截器链的 afterCompletion() 方法
if (mappedHandler != null) {
// E xception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
生成view
- DispatcherServlet.class
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.//1.设置response.locale
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
//2. 根据viewName 生成 对应的view 对象
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}//3. 渲染view
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
view.render()
- AbstractView.class ( 模板方法 , 最后合并model 和view 是到子类去做的)
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() +
", model " + (model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
// 1. 生成model
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
RedirectView 的具体做法
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws IOException {
String targetUrl = createTargetUrl(model, request);
targetUrl = updateTargetUrl(targetUrl, model, request, response);
// Save flash attributes
RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);
// Redirect// 调用这个东西response.sendRedirect(encodedURL);
sendRedirect(request, response, targetUrl, this.http10Compatible);
}
总结:
- 总流程:
- request 初始化 ( 判断 文件上传请求, 异步请求,request 与controller 绑定)
- request 执行流程(拦截器链HandlerExecutionChain, 方法参数处理,方法返回值处理. 生成modelandview)
- modelAndView 生成渲染页面 ( 重定向: response.sendRedirect() , 转发:request.getRequestDispatcher(“/“).forward(request,response);)
- 省略内容:
- 由于本次源码分析是结果是基于重定向的情况,所以少了**内容协商(比如返回值是json 字符串,需要进行类型转换) **, 有需要建议去参考链接查看. 我给出的源码解析较为简单,请见谅!
参考:
- https://www.yuque.com/atguigu/springboot/vgzmgh