简介

上一篇,我们分析了spring mvc启动过程的源码,这一节,来一起分析下在用户请求controller的过程中,spring mvc做了什么事?

一、准备

我写这么一个controller

package com.jacky.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod; @Controller
@RequestMapping("/bbb")
public class IndexController { @RequestMapping(value = "/test.do", method = RequestMethod.GET)
public String index() {
return "index";
} @RequestMapping(value = "/aaa.do", method = RequestMethod.GET)
@ResponseBody
public String aaa() {
return "aaa";
} }

二、用户请求controller的过程(http://localhost:8080/spring-mvc-demo/bbb/aaa.do)

上一篇,我们知道了,spring mcv基于servlet的,核心类是DispatcherServlet,那根据Servlet的知识,请求首先service()方法,但是在DispatherServlet中并没有找到service方法,在其父类FrameworkServlet中找到service方法,那我们就从这里看起。

 protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}

2.1、接下来我们看处理请求方法processRequest(request, response)

 protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doService(request, response);
}

2.2、接下来我们看看doService(request,response)方法

 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
doDispatch(request, response);
}

2.3、接下来我们看看DispatcherServlet的doDispatch(request,reponse)方法

 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
Exception dispatchException = null;
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); //根据请求获得请求执行链
mappedHandler = getHandler(processedRequest);
//根据处理器获得对应的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//调用controller中的method的方法之前先调用拦截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} //根据请求调用controller中的method,然后返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//调用spring mvc拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}

这个方法里面有很多很关键的方法,没事我们一个个看里面的实现细节

2.4、首先我们看看获得请求执行链的方法getHandler(processedRequest)

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//遍历spring mvc启动时初始化好的handlerMappings(类型为List<HandlerMapping>)
for (HandlerMapping hm : this.handlerMappings) {
//利用handlerMaping取得处理器执行链
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

2.5、接下来我们看看handlerMaping是怎么取得处理器执行链的?

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获得HandlerMethod对象
Object handler = getHandlerInternal(request);
//获得处理器执行链对象
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
return executionChain;
}

2.6、首先我来看看怎么获得HandleMethod对象的

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获得请求路径,这里是lookupPath="/bbb/aaa.do"
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//开启读锁
this.mappingRegistry.acquireReadLock();
try {
//根据url获得HandleMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//释放读锁
this.mappingRegistry.releaseReadLock();
}
}

2.7、接下来我们看看getHandlerExecutionChain是怎么获得处理器执行链的

 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
//往处理器执行链的List<HandlerInterceptor>类型的interceptorList集合中,存放拦截器
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}

从这里我们可以看到处理器其实封装了拦截器集合和一个handleMethod对象(封装了controller Class对象,浏览器请求的Controler中的method方法对象,参数)

2.8、上面,我们知道了,springMVC是怎么通过handleMaping获得handle(HandleMethod)对象,以及怎么添加拦截器,组装成处理器执行链,接下来我们继续看看springMVC是怎么获得处理器对应的

适配器的。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//遍历spring mvc初始化时,设置的DispatcherServlet的成员变量List<HandlerAdapter> handlerAdapters
for (HandlerAdapter ha : this.handlerAdapters) {
//判断适配器支不支持handler
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

因为有会遍历很多种适配器,从上面我们知道,handlerd的类型是HandleMethod,所以的我们看的是RequestMappingHandlerAdapter适配器,根据RequestMappingHandlerAdapter的继承关系

可以知道,RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapter,在ha.supports(handler)方法中,刚好在AbstractHandlerMethodAdapter类中,我们来看看

public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

就是判断传入的handler是否是属于HandlerMethod类型的,如果是,就返回true,然后RequestMappingHandlerAdapter对象

2.9、springMVC怎么获得处理请求的适配器我们看完了,接下来,我们看看,获得适配器后,是怎么调用到controller中的对应的method的?

 //根据请求调用controller中的method,然后返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
首先调用的是AbstractHandlerMethodAdapter类的handle方法
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception { return handleInternal(request, response, (HandlerMethod) handler);
}

可以看到这个方法没干什么,只是调用了handleInternal方法,那我们就看看这个方法

 protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}

这个方法主要是调了invokeHandlerMethod方法,那我们继续来看卡这个方法干了什么?

   protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); //把handlerMethod封装成ServletInvocableHandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//设置参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
//设置spring mvc请求controller的method返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
//设置参数名称发现器
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

3.0、接下来我们看看真正的调用和处理方法invocableMethod.invokeAndHandle(webRequest, mavContainer);

public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//在这里通过反射调用controller中的method方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest); if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
} mavContainer.setRequestHandled(false);
//在这里通过返回值处理器处理器进行二次处理,例如:如果加了方法加了reponseBody注解,就把结果序列化json字符串再返回
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest); }

spring mvc之请求过程源码分析的更多相关文章

  1. spring mvc之启动过程源码分析

    简介 这两个星期都在看spring mvc源码,看来看去还是还是很多细节没了解清楚,在这里把看明白的记录下,欢迎在评论中一起讨论. 一.铺垫 spring mvc是基于servlet的,在正式分析之前 ...

  2. Spring Boot与Spring MVC集成启动过程源码分析

    开源项目推荐 Pepper Metrics是我与同事开发的一个开源工具(https://github.com/zrbcool/pepper-metrics),其通过收集jedis/mybatis/ht ...

  3. spring mvc 启动过程及源码分析

    由于公司开源框架选用的spring+spring mvc + mybatis.使用这些框架,网上都有现成的案例:需要那些配置文件.每种类型的配置文件的节点该如何书写等等.如果只是需要项目能够跑起来,只 ...

  4. struts2请求过程源码分析

    Struts2是Struts社区和WebWork社区的共同成果,我们甚至可以说,Struts2是WebWork的升级版,他采用的正是WebWork的核心,所以,Struts2并不是一个不成熟的产品,相 ...

  5. 2、Spring的LocalSessionFactoryBean创建过程源码分析

    spring的LocalSessionFactoryBean生成过程与hibernate的SessionFactory生成过程是高度吻合的. 为了后面源码分析,首先讲解一个接口,一个类的功能:①.接口 ...

  6. struts2请求过程源码分析(转)

    Struts2是Struts社区和WebWork社区的共同成果,我们甚至 可以说,Struts2是WebWork的升级版,他采用的正是WebWork的核心,所以,Struts2并不是一个不成熟的产品, ...

  7. 【Spring boot】启动过程源码分析

    启动过程结论 推测web应用类型. spi的方式获取BootstrapRegistryInitializer.ApplicationContextInitializer.ApplicationCont ...

  8. Spring Cloud学习 之 Spring Cloud Ribbon(执行流程源码分析)

    Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 文章目录 分析: 总结: 分析: ​ 在上篇文章中,我们着重分析了RestTempla ...

  9. SpringMVC请求流程源码分析

    一.SpringMVC使用 1.工程创建 创建maven工程. 添加java.resources目录. 引入Spring-webmvc 依赖. <dependency> <group ...

随机推荐

  1. 轻量级验证码生成插件webutil-licenseImage源码与实例应用

    webutil-licenseImage 插件内置4种验证码样式,支持用户扩展.自定义样式实现简单验证码. 源码脱管地址: http://code.google.com/p/licenseimage/ ...

  2. 【Algorithm】插入排序

    一. 算法描述 插入排序具体算法描述如下: 从第一个元素开始,该元素可以认为已经被排序 取出下一个元素,在已经排序的元素序列中从后向前扫描 如果该元素(已排序)大于新元素,将该元素移到下一位置 重复步 ...

  3. lineman 的理念与 modern web app

    无意中翻到javascript 有个 lineman工具, 提供了一些脚手架 以及 默认的app目录结构,同时还附带了诸多前端的性能优化工具,在他的主页还发现其理念与我之前关于web app的开发模型 ...

  4. Android数据库安全解决方案,使用SQLCipher

    源码:http://files.cnblogs.com/android100/SQLCipherTest.rar 我们都知道,Android系统内置了SQLite数据库,并且提供了一整套的API用于对 ...

  5. Matlab 2016b 正式版下载

    Matlab 2016b  正式版下载 这两天为完成最优化大作业,zyy又开始鼓捣matlab了.之前我装的是matlab 2013a,发现不支持求解整数规划,遂更新了一下. 下载地址 链接:http ...

  6. Hbase/Hadoop Java API编程常用语句

    从scanner获取rowkey: for(Result rr : scanner){ String key =Bytes.toString(rr.getRow())} HBase API - Res ...

  7. 记一次mysql的存储过程改写

    最近在对公司以前的老项目做整理,发现以前同事在程序中许多模块都是多次调用几个分散的存储过程..这样做无疑消耗了连接池的连接数,甚至会导致连接不够的时候创建连接池导致数据库处理的消耗..以及到处调用连接 ...

  8. Atitit jquery  1.4--v1.11  v1.12  v2.0  3.0 的新特性

    Atitit jquery  1.4--v1.11  v1.12  v2.0  3.0 的新特性 1.1. Jquery1.12  jQuery 2.2 和 1.12 新版本发布 - OPEN资讯.h ...

  9. LeetCode263——Ugly Number

    Write a program to check whether a given number is an ugly number. Ugly numbers are positive numbers ...

  10. windows 内存管理的几种方式及其优缺点

    windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或者页面,然后把页式虚拟地 ...