web九大组件之---HandlerAdapter适配器模式实践源码分析【享学Spring MVC】

简介: web九大组件之---HandlerAdapter适配器模式实践源码分析【享学Spring MVC】

前言


如果说理解了HandlerMapping相当于掌握了Spring MVC的1/3,那么若你继续理解了HandlerAdapter(以及它的相关组件),那几乎可以说你就理解了它剩下的2/3了。


个人夸张划分,不喜勿喷


HandlerAdapter的作用:因为Spring MVC中的Handler可以是多种/4种形式,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为方法入参,那么如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情–> 适配。


它的作用是:根据 Handler 来找到支持它的 HandlerAdapter,通过 HandlerAdapter 执行这个 Handler 得到 ModelAndView 对象。


适配器模式简介


假如你有现在存在一个类的接口方法,但是这个接口不太符合你的预期(方法签名对应不上),如果要用他就需要在他的源码上进行一些修改,显然这个不可行。

这时还有一种方案:你可以做一个适配器,在不修改原来这个接口源码的情况下,在适配器上对这个接口进行运用,使得适配器符合你的接口规范。


其实生活上适配器有大量的应用,最为常见的就是电源适配器吧~


适配器模式(Adapter Pattern):把一个类的接口变换成客户所期待的另一种接口, Adapter模式使原本因接口不匹配(或者不兼容)而无法在一起工作的两个类能够在一起工作。

适配器模式又称为转换器模式、变压器模式、包装(Wrapper)器模式(把已有的一些类包装起来,使之能有满足需要的接口)。


以下情况可以使用适配器模式


  • 你想使用一个已经存在的类,而它的接口不符合你的需求(但你又不能修改器源码)
  • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作


HandlerAdapter就是利用适配器模式的一个实现,它在Spring MVC体系中的地位举足轻重。


HandlerAdapter


中文释义:Handler的适配器。JavaDoc解释为:MVC框架SPI,允许核心MVC工作流的参数化。

它必须为每个Handler程序都准备一个对应的适配器来处理请求,有了这个SPI接口,它能够让DispatcherServlet无限的扩展:能够兼容一切的Handler处理器类型。


DispatcherServlet就是通过这个接口来访问handler的,而不是直接去访问真正的实际的处理器,这样做的好处是大大的:


  • 处理器程序允许是任意的Object
  • 集成第三方请求处理器的时候,本处代码也无需修改


此接口不适用于应用程序开发人员。对于想要开发自己的web工作流的处理程序来说(或者你想进行深度定制),那就用它吧。

为何需要使用HandlerAdapter适配?


Spring MVC的Handler(Controller接口,HttpRequestHandler,Servlet、@RequestMapping)有四种表现形式,在Handler不确定是什么方式的时候(可能是方法、也可能是类),适配器这种设计模式就能模糊掉具体的实现,从而就能提供统一访问接口。

public interface HandlerAdapter {
  // 判断当前的这个HandlerAdapter  是否 支持给与的handler
  // 因为一般来说:每个适配器只能作用于一种处理器(你总不能把手机适配器拿去用于电脑吧)
  boolean supports(Object handler);
  // 核心方法:利用 Handler 处理请求,然后返回一个ModelAndView 
  // DispatcherServlet最终就是调用此方法,来返回一个ModelAndView的~
  @Nullable
  ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
  // 同HttpServlet 的 getLastModified方法
  // Can simply return -1 if there's no support in the handler class.
  long getLastModified(HttpServletRequest request, Object handler);
}

HandlerAdapter.supports()

HandlerAdapter.supports()方法的主要作用在于判断当前的HandlerAdapter是否能够支持当前的handler的适配。这里的handler是由HandlerExecutionChain HandlerMapping.getHandler(HttpServletRequest)方法获取到的。从这里可以看出:


  • HandlerMapping的作用主要是根据request请求匹配/映射上能够处理当前request的handler
  • HandlerAdapter的作用在于将request中的各个属性,如request param适配为handler能够处理的形式

- 参数绑定、数据校验、内容协商…几乎所有的web层问题都在在这里完成的。


HandlerAdapter.handle()


执行真正开发者开发的处理方法的地方。Spring MVC自动帮我们完成数据绑定、视图渲染等等一切周边工作~


HandlerAdapter.getLastModified()


获取当前请求的最后更改时间,主要用于供给浏览器判断当前请求是否修改过,从而判断是否可以直接使用之前缓存的结果


它的继承树:

image.png


从实现类的个数上看是不是很熟悉:4个,刚好对应着我们Handler内置的那四种实现方式~


由简到繁,逐个分析:


SimpleControllerHandlerAdapter


适配org.springframework.web.servlet.mvc.Controller这种Handler。它是一个非常古老的适配器(几乎已弃用状态):


Controller它没有对参数的自动封装、校验等一系列高级功能,但是它保留有对ModelAndView的处理能力,这是区别Servlet这种处理器的地方。

// 适配`org.springframework.web.servlet.mvc.Controller`这种Handler
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
  @Override
  public boolean supports(Object handler) {
    return (handler instanceof Controller);
  }
  // 最终执行逻辑的还是Handler啊~~~~
  @Override
  @Nullable
  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    return ((Controller) handler).handleRequest(request, response);
  }
  // 此处注意:若处理器实现了`LastModified`接口,那就委托给它了
  // 否则返回-1  表示不要缓存~
  @Override
  public long getLastModified(HttpServletRequest request, Object handler) {
    if (handler instanceof LastModified) {
      return ((LastModified) handler).getLastModified(request);
    }
    return -1L;
  }
}


源码非常之简单,因为它直接处理的就是源生的HttpServletRequest和HttpServletResponse,所以它和Servlet容器是强绑定的。无数据自动封装、校验等一系列高级功能,所以实际应用中此种方式很少被使用。


画外音:Spring5.0后的WebFlux基于Reactive模式是不支持这种Handler的~


HttpRequestHandlerAdapter

适配org.springframework.web.HttpRequestHandler这种Handler。它比Controller方式还源生:


// @since 2.0
public class HttpRequestHandlerAdapter implements HandlerAdapter {
  @Override
  public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
  }
  @Override
  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {\
    ((HttpRequestHandler) handler).handleRequest(request, response);
    return null;
  }
  @Override
  public long getLastModified(HttpServletRequest request, Object handler) {
    if (handler instanceof LastModified) {
      return ((LastModified) handler).getLastModified(request);
    }
    return -1L;
  }
}


和上面的唯一不同是:return null。那是因为HttpRequestHandler#handleRequest()它没有返回值(全靠开发者自己写response),而Controller最起码来说还有Model和View自动渲染的能力嘛~


SimpleServletHandlerAdapter

适配javax.servlet.Servlet这种Handler。


public class SimpleServletHandlerAdapter implements HandlerAdapter {
  @Override
  public boolean supports(Object handler) {
    return (handler instanceof Servlet);
  }
  @Override
  @Nullable
  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ((Servlet) handler).service(request, response);
    return null;
  }
  @Override
  public long getLastModified(HttpServletRequest request, Object handler) {
    return -1;
  }
}


javax.servlet.Servlet的处理方式几乎同HttpRequestHandler,都是对源生请求进行直接处理。它的特殊之处在于:Spring MVC默认并不向容器注册这种HandlerAdapter,若需要使用是需要调用者手动给注册这个Bean,Servlet这种Handler才能正常使用的~


AbstractHandlerMethodAdapter

从命名中其实就可以看出端倪,它主要是支持到了org.springframework.web.method.HandlerMethod这种处理器,显然这种处理器也是我们最最最最为常用的。


// @since 3.1 @RequestMapping注解是Spring2.5出现的
// 注意:它实现了Ordered接口
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
  // 唯一构造函数。传的false表示:忽略掉supportedMethods这个属性
  // 默认它的值是GET、POST、HEAD(见WebContentGenerator)
  public AbstractHandlerMethodAdapter() {
    // no restriction of HTTP methods by default
    super(false);
  }
  // 只处理HandlerMethod 类型的处理器。抽象方法supportsInternal默认返回true
  // 是流出的钩子可以给你自己扩展的
  @Override
  public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
  }
  // 抽象方法交给子类handleInternal去实现
  @Override
  @Nullable
  public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return handleInternal(request, response, (HandlerMethod) handler);
  }
  ...
}


它有个大名鼎鼎的子类:RequestMappingHandlerAdapter。此子类已经把HandlerMethod的实现精确到了@RequestMapping注解方案。这个HandlerAdapter可谓是Spring MVC的精华之所在,只要理解了它以及它的相关组件,就基本可以自信地说自己掌握了Spring MVC。


因为这部分内容过于复杂繁多,因此我撰了专文来描述它,详情请点击这里


DispatcherServlet#doDispatch分发流程

主要看看DispatcherServlet是如何使用HandlerAdapter的


DispatcherServlet:
  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ...
    //1、根据URL(当然不一定非得是URL)匹配到一个处理器
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
      // 若匹配不到Handler处理器,就404了
      noHandlerFound(processedRequest, response);
      return;
    }
    //2、从Chain里拿出Handler(注意是Object类型哦~ )然后找到属于它的适配器
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    ...
    //3、执行作用在此Handler上的所有拦截器的Pre方法
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
      return;
    }
    //4、真正执行handle方法(也就是你自己书写的逻辑方法),得到一个ModelAndView
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    //5、视图渲染
    applyDefaultViewName(processedRequest, mv);
    //6、执行拦截器的post方法(可见它是视图渲染完成了才会执行的哦~)
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    ...
    //7、执行拦截器的afterCompletion方法(不管抛出与否)
  }


从执行步骤中可以看到:HandlerAdapter对于执行流程的通用性起到了非常重要的作用,它能把任何一个处理器(Object)都适配成一个HandlerAdapter,从而可以做统一的流程处理,这也是为何DispatcherServlet它能作为其它web处理框架的分发器的原因(因为它没有耦合具体的处理器,你完全可以自己去实现)~


相关阅读

web九大组件之—RequestMappingHandlerAdapter详尽解析【享学Spring MVC】


总结


本文介绍Spring MVC在处理请求时使用的适配器模式实践HandlerAdapter,感受到了它对DispatcherServlet的重要性。适配器模式在基础框架设计中属常用的一种方式,比如Spring AOP中也有用到,具体请理解我上面说的两个使用场景。

本文留下一个最最最为重要的子类RequestMappingHandlerAdapter没有放在本文讲述,只因为它过于重要和复杂,请务必出门左拐详细了解下它,传送门在此

相关文章
|
20天前
|
缓存 安全 Java
《深入理解Spring》过滤器(Filter)——Web请求的第一道防线
Servlet过滤器是Java Web核心组件,可在请求进入容器时进行预处理与响应后处理,适用于日志、认证、安全、跨域等全局性功能,具有比Spring拦截器更早的执行时机和更广的覆盖范围。
|
6月前
|
前端开发 算法 API
构建高性能图像处理Web应用:Next.js与TailwindCSS实践
本文分享了构建在线图像黑白转换工具的技术实践,涵盖技术栈选择、架构设计与性能优化。项目采用Next.js提供优秀的SSR性能和SEO支持,TailwindCSS加速UI开发,WebAssembly实现高性能图像处理算法。通过渐进式处理、WebWorker隔离及内存管理等策略,解决大图像处理性能瓶颈,并确保跨浏览器兼容性和移动设备优化。实际应用案例展示了其即时处理、高质量输出和客户端隐私保护等特点。未来计划引入WebGPU加速、AI增强等功能,进一步提升用户体验。此技术栈为Web图像处理应用提供了高效可行的解决方案。
|
7月前
|
NoSQL 安全 Java
深入理解 RedisConnectionFactory:Spring Data Redis 的核心组件
在 Spring Data Redis 中,`RedisConnectionFactory` 是核心组件,负责创建和管理与 Redis 的连接。它支持单机、集群及哨兵等多种模式,为上层组件(如 `RedisTemplate`)提供连接抽象。Spring 提供了 Lettuce 和 Jedis 两种主要实现,其中 Lettuce 因其线程安全和高性能特性被广泛推荐。通过手动配置或 Spring Boot 自动化配置,开发者可轻松集成 Redis,提升应用性能与扩展性。本文深入解析其作用、实现方式及常见问题解决方法,助你高效使用 Redis。
690 4
|
2月前
|
前端开发 Java 开发者
MVC 架构模式技术详解与实践
本文档旨在全面解析软件工程中经典且至关重要的 MVC(Model-View-Controller) 架构模式。内容将深入探讨 MVC 的核心思想、三大组件的职责与交互关系、其优势与劣势,并重点分析其在现代 Web 开发中的具体实现,特别是以 Spring MVC 框架为例,详解其请求处理流程、核心组件及基本开发实践。通过本文档,读者将能够深刻理解 MVC 的设计哲学,并掌握基于该模式进行 Web 应用开发的能力。
338 1
|
8月前
|
安全 Java 数据安全/隐私保护
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 三大核心组件
本课程介绍如何在Spring Boot中集成Shiro框架,主要讲解Shiro的认证与授权功能。Shiro是一个简单易用的Java安全框架,用于认证、授权、加密和会话管理等。其核心组件包括Subject(认证主体)、SecurityManager(安全管理员)和Realm(域)。Subject负责身份认证,包含Principals(身份)和Credentials(凭证);SecurityManager是架构核心,协调内部组件运作;Realm则是连接Shiro与应用数据的桥梁,用于访问用户账户及权限信息。通过学习,您将掌握Shiro的基本原理及其在项目中的应用。
279 0
|
8月前
|
负载均衡 Java Nacos
Spring Cloud五大组件
Spring Cloud五大组件
|
4月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
286 0
|
5月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:路由、中间件、参数校验
Gin框架以其极简风格、强大路由管理、灵活中间件机制及参数绑定校验系统著称。本文详解其核心功能:1) 路由管理,支持分组与路径参数;2) 中间件机制,实现全局与局部控制;3) 参数绑定,涵盖多种来源;4) 结构体绑定与字段校验,确保数据合法性;5) 自定义校验器扩展功能;6) 统一错误处理提升用户体验。Gin以清晰模块化、流程可控及自动化校验等优势,成为开发者的优选工具。
|
5月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:使用 Gin 快速构建 Web 服务
Gin 是一个高效、轻量级的 Go 语言 Web 框架,支持中间件机制,非常适合开发 RESTful API。本文从安装到进阶技巧全面解析 Gin 的使用:快速入门示例(Hello Gin)、定义 RESTful 用户服务(增删改查接口实现),以及推荐实践如参数校验、中间件和路由分组等。通过对比标准库 `net/http`,Gin 提供更简洁灵活的开发体验。此外,还推荐了 GORM、Viper、Zap 等配合使用的工具库,助力高效开发。
|
4月前
|
JSON 前端开发 Java
Spring MVC 核心组件与请求处理机制详解
本文解析了 Spring MVC 的核心组件及请求流程,核心组件包括 DispatcherServlet(中央调度)、HandlerMapping(URL 匹配处理器)、HandlerAdapter(执行处理器)、Handler(业务方法)、ViewResolver(视图解析),其中仅 Handler 需开发者实现。 详细描述了请求执行的 7 步流程:请求到达 DispatcherServlet 后,经映射器、适配器找到并执行处理器,再通过视图解析器渲染视图(前后端分离下视图解析可省略)。 介绍了拦截器的使用(实现 HandlerInterceptor 接口 + 配置类)及与过滤器的区别
291 0

热门文章

最新文章