SpringMVC 4.3 源码分析之 HandlerAdapter

1. HandlerAdapter 概述

在 SpringMVC 中Handler可以是各种类型的类, 而DispatcherServlet肯定需要对 handler 进行激活, 所以这时需要一个适配类, 将各种handler适配成可以执行的类, 这时 HandlerAdapter就出现了; 主要代码如下

// Handler 处理适配器, 适配不同的 Handler
public interface HandlerAdapter {

    /** HandlerAdapter 是否支持这个 handler <-- 通常是检查这个 handler 是否是某一类
     *  如
     *    SimpleServletHandlerAdapter      -->  javax.servlet.Servlet
     *    SimpleControllerHandlerAdapter   -->  org.springframework.web.servlet.mvc.Controller
     *    HttpRequestHandlerAdapter        -->  org.springframework.web.HttpRequestHandler
     *    RequestMappingHandlerAdapter     -->  handler instanceof HandlerMethod
     * PS: 整个 DispatcherServlet 的入口是 HandlerAdapter.handle, 但其中真正执行的可能是  Servlet, Controller, HttpRequestHandler, HandlerMethod; 这时就需要 HandlerAdapter 来进行适配操作
     */
    boolean supports(Object handler);

    // 处理 HttpServletRequest 的入口方法
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    // Http 请求中的lastModifiedTime, 默认 -1
    long getLastModified(HttpServletRequest request, Object handler);
}

而 HandlerAdapter具有如下子类:


HandlerAdapter.png
1. SimpleServletHandlerAdapter:    适配实现 Servlet 接口的 Handler, 默认调用其 service 方法
2. SimpleControllerHandlerAdapter: 适配实现 Controller 接口的 Handler, 默认调用其 handleRequest 方法
3. HttpRequestHandlerAdapter:      适配实现 HttpRequestHandler 接口的 Handler, 默认调用其 handleRequest 方法
4. RequestMappingHandlerAdapter:   适配被@RequestMapping注释的方式, 一般都是解析一个一个参数, 并且通过反射进行激活

PS: 上面几个子类中只有 RequestMappingHandlerAdapter有复杂的逻辑, 也是使用最频繁的 HandlerAdapter

2. HandlerAdapter 的子类 SimpleServletHandlerAdapter

SimpleServletHandlerAdapter 适配 Servlet 调用的HandlerAdapter, 代码如下:

public boolean supports(Object handler) {
    return (handler instanceof Servlet); // 只要是个 Servlet, 就支持
}

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    // 直接调用 Servlet 的 service 接口返回
    ((Servlet) handler).service(request, response);
    return null;
}
3. HandlerAdapter 的子类 SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter 适配 Controller 接口的 HandlerAdapter, 代码如下:

public boolean supports(Object handler) {
    // 判断将要调用的 handler 是不是 Controller
    return (handler instanceof Controller);
}

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    // 调用 Controller 接口类的接口方法 handleRequest() 方法直接响应给前端
    return ((Controller) handler).handleRequest(request, response);
}
4. HandlerAdapter 的子类 HttpRequestHandlerAdapter

HttpRequestHandlerAdapter 适配 HttpRequestHandler 的 HandlerAdapter, 代码如下:

public boolean supports(Object handler) {
    // 判断将要调用的 handler 是不是 HttpRequestHandler
    return (handler instanceof HttpRequestHandler);
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 调用 HttpRequestHandler 接口类的接口方法 handleRequest() 方法直接响应给前端
    ((HttpRequestHandler) handler).handleRequest(request, response);
    return null;
}
5. RequestMappingHandlerAdapter 概述

RequestMappingHandlerAdapter 将被@RequestMapping注释修饰的方法封装成ServletInvocableHandlerMethod, 并进行激活, 其主要有如下组件:

// 参数解析器 HandlerMethodArgumentResolver, 比如 RequestResponseBodyMethodProcessor
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
// 组合模式的参数解析器
private HandlerMethodArgumentResolverComposite argumentResolvers;
// initBinderArgumentResolvers 的 参数解析器
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
// 返回值处理器 比如 RequestResponseBodyMethodProcessor
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
// 组合模式的 返回值解析器
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
// ModelAndView 解析器
private List<ModelAndViewResolver> modelAndViewResolvers;
// 内容解析器 <-- 一般通过 HttpServletRequest 获取 MediaType
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
// HttpMessageConverter 转换器 --> 比如 form 表单的 FormHttpMessageConverter, Json 对应的 MappingJackson2HttpMessageConverter
private List<HttpMessageConverter<?>> messageConverters;
// request|response 对应的 Advice <-- 其实就像 AOP 中的 Advice的概念
private List<Object> requestResponseBodyAdvice = new ArrayList<Object>();

// 是否给予 Session 同步请求
private boolean synchronizeOnSession = false;
// Session 中存储|获取器
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
// 方法解析器, 默认使用 ASM | 反射来获取 <- 反射针对 Java 8
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
// BeanFactory
private ConfigurableBeanFactory beanFactory;

上面每个组件都有其对应的作用: 其中主要有 HandlerMethod 的参数解析器HandlerMethodArgumentResolver, 返回值处理器 HandlerMethodReturnValueHandler, 请求内容的格式解析器 ContentNegotiationManager, 知道了请求内容的格式,这时需要将字符串转为对应方法参数的参数转换器 HttpMessageConverter, Controller增强器 requestResponseBodyAdvice(这其实就像Aop中的Advice), 再者我们可以通过 @RequestParam 获取指定名称的参数内容, 所以还需要一个参数名称获取器 ParameterNameDiscoverer(有基于ASM, 有基于反射机制); 首先我们来看一下 RequestMappingHandlerAdapter 的初始化, 对应的初始化入口在 InitializingBean.afterPropertiesSet 中进行:

public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    // 初始化 被 @ControllerAdvice 修饰的类 <-- 这是一个 Controller 增强器, 主要是通过 @ModelAttribute, @InitBinder 注解
    initControllerAdviceCache(); 
    // 初始化 HandlerMethodArgumentResolver, 最后封装成 HandlerMethodArgumentResolverComposite <-- 组合模式
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);  // Spring 里面 composite 模式 的提现
    }
    // 初始化 参数绑定方法的参数解决器  <-- 这个平时用得比较少
    if (this.initBinderArgumentResolvers == null) { 
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    // 初始化 HandlerMethodReturnValueHandler 比如 -> RequestResponseBodyMethodProcessor
    if (this.returnValueHandlers == null) { 
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

其上主要还是做了:

1. 初始化 被 @ControllerAdvice 修饰的类 <-- 这是一个 Controller 增强器, 主要是通过 @ModelAttribute, @InitBinder 注解
    1.1 收集 ApplicationContext 中所有被 @ControllerAdvice 注解修饰的 Bean, 并封装成 ControllerAdviceBean
    1.2 获取 Bean 上被 @ModelAttribute @RequestMapping 修饰的方法
    1.3 将被 @ModelAttribute @RequestMapping 修饰的方法 放入 modelAttributeAdviceCache
    1.4 获取 Bean 上被 @InitBinder 注解的方法, 并放入 initBinderAdviceCache 中
    1.5 若是 RequestBodyAdvice 子类, 则加入 requestResponseBodyAdviceBeans
    1.6 若是 RequestBodyAdvice 子类, 则加入 requestResponseBodyAdviceBeans
2. 初始化 HandlerMethodArgumentResolver(这里主要分成 基于注解的参数解决器, 基于参数类型的解决器), 最后封装成 HandlerMethodArgumentResolverComposite <-- 组合模式
3. 初始化 参数绑定方法的参数解决器(这里主要分成 基于注解的参数解决器, 基于参数类型的解决器), 最后封装成 HandlerMethodArgumentResolverComposite <-- 组合模式  <-- 这个平时用得比较少
4. 初始化 HandlerMethodReturnValueHandler(这里主要分成 基于注解的参数解决器, 单|多个目的的解决器) 比如 -> RequestResponseBodyMethodProcessor, 最后封装成 HandlerMethodReturnValueHandlerComposite <-- 组合模式

上面的参数解析器其实在 SpringMVC 中扮演着非常重要的角色, 也是 SpringMVC 扩展点的一个体现机制, 主要用到了如下的解析器:

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { // 获取默认的 HandlerMethodArgumentResolver
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
    // 基于注解的参数解析 <-- 解析的数据来源主要是 HttpServletRequest | ModelAndViewContainer
    // Annotation-based argument resolution
    // 解析被注解 @RequestParam, @RequestPart 修饰的参数, 数据的获取通过 HttpServletRequest.getParameterValues
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    // 解析被注解 @RequestParam 修饰, 且类型是 Map 的参数, 数据的获取通过 HttpServletRequest.getParameterMap
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    // 解析被注解 @PathVariable 修饰, 数据的获取通过 uriTemplateVars, 而 uriTemplateVars 却是通过 RequestMappingInfoHandlerMapping.handleMatch 生成, 其实就是 uri 中映射出的 key <-> value
    resolvers.add(new PathVariableMethodArgumentResolver());
    // 解析被注解 @PathVariable 修饰 且数据类型是 Map, 数据的获取通过 uriTemplateVars, 而 uriTemplateVars 却是通过 RequestMappingInfoHandlerMapping.handleMatch 生成, 其实就是 uri 中映射出的 key <-> value
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    // 解析被注解 @MatrixVariable 修饰, 数据的获取通过 URI提取了;后存储的 uri template 变量值
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    // 解析被注解 @MatrixVariable 修饰 且数据类型是 Map, 数据的获取通过 URI提取了;后存储的 uri template 变量值
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    // 解析被注解 @ModelAttribute 修饰, 且类型是 Map 的参数, 数据的获取通过 ModelAndViewContainer 获取, 通过 DataBinder 进行绑定
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    // 解析被注解 @RequestBody 修饰的参数, 以及被@ResponseBody修饰的返回值, 数据的获取通过 HttpServletRequest 获取, 根据 MediaType通过HttpMessageConverter转换成对应的格式, 在处理返回值时 也是通过 MediaType 选择合适HttpMessageConverter, 进行转换格式, 并输出
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    // 解析被注解 @RequestPart 修饰, 数据的获取通过 HttpServletRequest.getParts()
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    // 解析被注解 @RequestHeader 修饰, 数据的获取通过 HttpServletRequest.getHeaderValues()
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    // 解析被注解 @RequestHeader 修饰且参数类型是 Map, 数据的获取通过 HttpServletRequest.getHeaderValues()
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    // 解析被注解 @CookieValue 修饰, 数据的获取通过 HttpServletRequest.getCookies()
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    // 解析被注解 @Value 修饰, 数据在这里没有解析
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    // 解析被注解 @SessionAttribute 修饰, 数据的获取通过 HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_SESSION)
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    // 解析被注解 @RequestAttribute 修饰, 数据的获取通过 HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_REQUEST)
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    // 解析固定类型参数(比如: ServletRequest, HttpSession, InputStream 等), 参数的数据获取还是通过 HttpServletRequest
    resolvers.add(new ServletRequestMethodArgumentResolver());
    // 解析固定类型参数(比如: ServletResponse, OutputStream等), 参数的数据获取还是通过 HttpServletResponse
    resolvers.add(new ServletResponseMethodArgumentResolver());
    // 解析固定类型参数(比如: HttpEntity, RequestEntity 等), 参数的数据获取还是通过 HttpServletRequest
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    // 解析固定类型参数(比如: RedirectAttributes), 参数的数据获取还是通过 HttpServletResponse
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    // 解析固定类型参数(比如: Model等), 参数的数据获取通过 ModelAndViewContainer
    resolvers.add(new ModelMethodProcessor());
    // 解析固定类型参数(比如: Model等), 参数的数据获取通过 ModelAndViewContainer
    resolvers.add(new MapMethodProcessor());
    // 解析固定类型参数(比如: Errors), 参数的数据获取通过 ModelAndViewContainer
    resolvers.add(new ErrorsMethodArgumentResolver());
    // 解析固定类型参数(比如: SessionStatus), 参数的数据获取通过 ModelAndViewContainer
    resolvers.add(new SessionStatusMethodArgumentResolver());
    // 解析固定类型参数(比如: UriComponentsBuilder), 参数的数据获取通过 HttpServletRequest
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }
    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));
    return resolvers;
}

上面的解析器主要分成基于注解, 基于参数类型, 与自定义的参数解析器(PS: 大体上的数据来源是 HttpServletRequest的 header|body 中的属性, ModelAndViewContainer)! 然后就是处理返回值的处理器:

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();

    // Single-purpose return value types
    // 支持 ModelAndView 类型的 HandlerMethodReturnValueHandler, 最后将数据写入 ModelAndViewContainer
    handlers.add(new ModelAndViewMethodReturnValueHandler());
    // 支持 Map 类型的 HandlerMethodReturnValueHandler, 最后将数据写入 ModelAndViewContainer
    handlers.add(new ModelMethodProcessor());
    // 支持 View 类型的 HandlerMethodReturnValueHandler, 最后将数据写入 ModelAndViewContainer
    handlers.add(new ViewMethodReturnValueHandler());
    // 支持 ResponseEntity 类型的 HandlerMethodReturnValueHandler, 最后将数据写入 HttpServletResponse 的数据流中 OutputStream
    handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
    // 支持 ResponseEntity | StreamingResponseBody 类型的 HandlerMethodReturnValueHandler, 最后将数据写入 HttpServletResponse 的数据流中 OutputStream
    handlers.add(new StreamingResponseBodyReturnValueHandler());
    // 支持 HttpEntity | !RequestEntity 类型的 HandlerMethodReturnValueHandler, 最后将数据写入 HttpServletResponse 的数据流中 OutputStream
    handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));
    // 支持 HttpHeaders 类型的 HandlerMethodReturnValueHandler, 最后将数据写入 HttpServletResponse 的数据头部
    handlers.add(new HttpHeadersReturnValueHandler());
    // 支持 Callable 类型的 HandlerMethodReturnValueHandler
    handlers.add(new CallableMethodReturnValueHandler());
    handlers.add(new DeferredResultMethodReturnValueHandler());
    // 支持 WebAsyncTask 类型的 HandlerMethodReturnValueHandler
    handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

    // Annotation-based return value types
    // 将数据加入 ModelAndViewContainer 的 HandlerMethodReturnValueHandler
    handlers.add(new ModelAttributeMethodProcessor(false));
    // 返回值被 ResponseBody 修饰的返回值, 并且根据 MediaType 通过 HttpMessageConverter 转化后进行写入数据流中
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));

    // Multi-purpose return value types
    // 支持返回值为 CharSequence 类型, 设置 ModelAndViewContainer.setViewName
    handlers.add(new ViewNameMethodReturnValueHandler());
    // 支持返回值为 Map, 并将结果设置到 ModelAndViewContainer
    handlers.add(new MapMethodProcessor());
    // Custom return value types
    if (getCustomReturnValueHandlers() != null) {
        handlers.addAll(getCustomReturnValueHandlers());
    }
    // Catch-all
    if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
        handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
    }
    else {
        handlers.add(new ModelAttributeMethodProcessor(true));
    }
    return handlers;
}

在有了这些工具类后, 我们就可以完成AbstractHandlerMethodAdapter中定义的抽象方法handleInternal, 在 handleInternal 中主要进行了并发的控制(并发的粒度是 HttpSession), 而正真的方法激活交给了 ServletInvocableHandlerMethod, 对 ServletInvocableHandlerMethod 的初始化在 invokeHandlerMethod 中:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    // 构建 ServletWebRequest <-- 主要由 HttpServletRequest, HttpServletResponse
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        // 构建 DataBinder 工厂
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        // binderFactory 中存储着被 @InitBinder, @ModelAttribute 修饰的方法 <- 最终包裹成 InvocableHandlerMethod
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
        // 构建一个 ServletInvocableHandlerMethod
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        // 设置方法参数解析器 HandlerMethodArgumentValueResolver
        invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        // 返回值处理器 HandlerMethodReturnValueHandler
        invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        // 设置 WebDataBinderFactory
        invocableMethod.setDataBinderFactory(binderFactory);
        // 设置 参数名解析器
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        // 获取 HttpServletRequest 中存储的 FlashMap
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        // 这里是激活 @ModelAttribute, @InitBinder 方法, 并将返回值放入 ModelAndViewContainer
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
        // 将 HttpServletRequest 转换成方法的参数, 激活方法, 最后 通过 HandlerMethodReturnValueHandler 来处理返回值
        invocableMethod.invokeAndHandle(webRequest, mavContainer);      
        // 生成 ModelAndView
        return getModelAndView(mavContainer, modelFactory, webRequest); 
    }
    finally {
        webRequest.requestCompleted(); // 标志请求已经结束, 进行一些生命周期回调函数的激活
    }
}

代码到这边HandlerMethod还是没有激活, 还有非常重要的工作需要去做 -> 参数的解析与绑定 + 反射激活方法 + 返回值处理; 参数解析 + 绑定的操作在 InvocableHandlerMethod中, 具体流程如下:

private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
    // 这里的 getMethodParameters 其实是在构造 MethodParameters 时创建的
    MethodParameter[] parameters = getMethodParameters();           
    Object[] args = new Object[parameters.length];
    // 从这里开始对参数进行一个一个解析 <- 主要是通过 HandlerMethodArgumentResolver
    for (int i = 0; i < parameters.length; i++) {                   
        MethodParameter parameter = parameters[i];
        // 设置参数名解析器 ParameterNameDiscoverer (默认通过 ASM)
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        // 这一步是尝试 判断 参数 providedArgs 里面的类型是否满足 parameter 
        args[i] = resolveProvidedArgument(parameter, providedArgs);             
        if (args[i] != null) continue;
        // 判断是否 argumentResolvers 里面含有对应的 解释器
        if (this.argumentResolvers.supportsParameter(parameter)) {              
            try { // 通过 argumentResolvers 解析 HandlerMethod 里面 对应的参数内容
                args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                continue;
            }
            catch (Exception ex) {throw ex;}
        }
    }
    return args;
}

再得到参数后其实就可以激活对应的方法, 而返回值的做法相对上面的参数解析要简单一点, 都是先获取对应的返回值处理, 然后进行处理!

6. HandlerAdapter 中的优秀设计
1. 模版模式: 比如在 AbstractHandlerMethodAdapter中的 handleInternal, 而正真的实现在RequestMappingHandlerAdapter
2. 策略模式: 根据不同的策略实现接口 HandlerAdapter, 以支持不同的"controller"
3. InitializingBean: 这是 BeanFactory 中Bean生命周期的扩展接口, 表示 Bean 的初始化方法, RequestMappingHandlerAdapter 就是通过这个接口实现工具类的初始化
4. MethodIntrospector: 这是一个对方法进行反省操作的工具类, 通过传入 classType 与 methodFilter 来过滤需要的 Method
7. 总结

HandlerAdapter 是 SpringMVC中扩展机制的非常好的一个体现, 通过 HandlerAdapter, DispatcherServlet 就可以支持任何格式的Handler(这里的可以支持指在不改变 DispatcherServlet 的情况下), 第二是HandlerAdapter 基于不同Handler实现不同实现类(可以把它理解为策略模式), 最后也是最重要的就是参数的解析与返回值的解析(这个在下面进行叙述)!

8. 参考资料

SpringMVC源码分析系列
Spring MVC源码剖析
Spring源码情操陶冶
Spring 揭秘
Spring 技术内幕
Spring 源码深度分析
看透 Spring MVC 源代码分析与实践

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容