Spring MVC 中的适配器模式

文章目录

  • Spring MVC 中的适配器模式
    • 为什么不直接调用?
    • 解决方案一:统一 Controller
    • 解决方案二:使用适配器模式
    • DispatcherServlet 对 Adpater 的使用

Spring MVC 中的适配器模式

为什么不直接调用?

DispatcherServlet 为什么不直接调用 Controller ,而是通过 Adapter 来『间接』调用 Controller ?

原因在于:Spring MVC 现在对于 Controller 并没有『统一』的要求

Spring MVC 不强求 Controller 必须继承某个类,或实现某个接口。这样就导致,各个 Controller 中的『请求处理方法』的『样子长的并不一样』,这样,DispatcherServlet 在做请求的分发时,无法写出一致性代码,只能『具体情况具体分析』,例如:

// 假设 HandlerMapping 直接返回的就是 Controller 。
Handler handler = handlerMapping.getHandler();if (handler instanceof AHandler) {  ((AHandler)handler).xxx();
} 
else if (handler instanceof BHandler) { ((BHandler)handler).yyy();
} 
else if (handler instanceof CHandler) {((CHandler)handler).zzz();
} 
else if (...) {  ...  
}  
...

更要命的是,当你去自定义一个 Controller 之后,你需要去改 Spring MVC 的 DispatcherServlet 的源码!!! 去多加一个 else if (...) { ... }

很显然,这么干是不可能的。

这违反了设计模式中的开闭原则:对扩展开放,对修改关闭。

解决方案一:统一 Controller

上述问题的最简单直接的解决方案,就如同 Servlet API 一样,定义处一套统一的接口,要求 Spring MVC 框架的使用在在自定义 Controller 时,必须实现这个接口。这样就约定了每种 Controller 都有一种统一的使用方式。

早期的 Spring MVC 就是这么干的,当然,现在你也可以按照这种方式来使用 Spring MVC 框架。

public class HelloController implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {...return mav;}
}

这样,DispacherServlet 中的代码就能简化成如下这样:

// 假设 HandlerMapping 直接返回的就是 Controller 。
Handler handler = handlerMapping.getHandler();handler.handleRequest(req, resp);

这么干的缺点在于使用时不灵活,Spring MVC 框架的使用者受限于 Controller 接口,必须把代码写成『这种样子』。

因此,Spring MVC 并没有采取这种解决办法,而是采用的下面这种方案。

解决方案二:使用适配器模式

使用适配器模式的好处在于,Controller 可以『放飞自我』,Spring MVC 的使用者可以不受 Controller 接口的限制,以更方便的方式来编写自定义的 Controller 类。

DispacherServlet 如何调用 Controller 的问题,就由适配器来解决。

使用 Adapter 模式有 2 点需要注意的:

  1. Adapter 和 Controller 总是成对出现的。也就是说,Controller 的作者写 Controller 时『放飞自我』的代价就是,他要『多』写个 Adapter 。当然,由于 Adapter 可能会具有通用性,因此,Controller 类和 Adapter 类的数量关系也不至于是 1 : 1 的关系。

  2. Controller 的编写虽然可以『放飞自我』,但是 Adapter 的编写则是有要求的。

以下代码是伪代码,大家重点体会 Adapter 的用法和使用场景。

Spring MVC 会给出 Adapter 的接口要求:

public interface HandlerAdapter {  public boolean supports(Object handler);  public void handle(Object handler);  
}  
  • .supports 方法的作用在于表示本 Adapter 支持调用哪个/哪些 Controller 。

  • .handle 方法的作用是 DispatcherServlet 和 Controller 之间的调用的『桥梁』。

Controller 的作者基于 Adapter 接口就可以写出与上面三个 Controller 配套的用于自家 Controller 的 Adapater:

// 适用于 AController 的 Adapter
public class AHandlerAdapter implements HandlerAdapter {  public void handle(Object handler) {  ((AController)handler).xxx();  }  public boolean supports(Object handler) {  return (handler instanceof AController);  }  
}  // 适用于 BController 的 Adapter
public class BHandlerAdapter implements HandlerAdapter {  public void handle(Object handler) {  ((BController)handler).yyy();  }  public boolean supports(Object handler) {  return (handler instanceof BController);  }  
}  // 适用于 CController 的 Adapter
public class CHandlerAdapter implements HandlerAdapter {  public void handle(Object handler) {  ((CController)handler).zzz();  }  public boolean supports(Object handler) {  return (handler instanceof CController);  }  }  

这样依赖,虽然 AController、BController 和 CController 的请求处理方法『长得不一样』,但是它们各自的 Adapter『长得一样』。

这样一来,DispacherServlet 向 HandlerMapping 要到的是 Adapter ,那么它就可以用一种统一的方式调用 Adapter,而各个 Adapter 再去调用各自适配的 Controller 。

DispatcherServlet 对 Adpater 的使用

模拟 DispatcherServlet

public class DispatchServlet {  public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();   public DispatchServlet(){  handlerAdapters.add(new AHandlerAdapter());  handlerAdapters.add(new BHandlerAdapter());  handlerAdapters.add(new CHandlerAdapter());  }public void doDispatch() {  AController controller = new AController();  
//      BController controller = new BController();  
//      CController controller = new CController();  //得到对应适配器  HandlerAdapter adapter = getHandler(controller);  //通过适配器执行对应的controller对应方法  adapter.handle(controller);  }  public HandlerAdapter getHandler(Controller controller){  for (HandlerAdapter adapter: handlerAdapters) {  if (adapter.supports(controller)) {  return adapter;  }  }return null;  }  public static void main(String[] args) {new DispatchServlet().doDispatch();  }  }  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/816929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【入门】时钟旋转

时间限制 : 1 秒 内存限制 : 128 MB 时钟上面的时针从m时走到n时旋转了多少度&#xff1f;&#xff08;m<n&#xff0c;且m和n都是1~12之间的整数&#xff09; 输入 2个整数m和n 输出 一个整数代表时针旋转的度数 样例 输入 1 4 输出 90 提示 基础问题 #includ…

Python爬取天气信息,并进行语音播报

1、涉及的主要库 requests&#xff1a;requests 是一个Python的HTTP库&#xff0c;用于发送HTTP请求和处理响应。它是基于Python的 urllib 和 httplib 库的封装&#xff0c;提供了更加简洁、易用的接口&#xff0c;使得发送HTTP请求变得更加方便。你可以使用 requests 库来获取…

SQL语法 case when语句用法讲解

CASE WHEN解释 &#xff1a; SQL中的CASE WHEN语句是一种条件表达式&#xff0c;它允许你根据不同的情况返回不同的值。CASE WHEN通常用于SELECT语句中&#xff0c;用于创建新的列&#xff0c;该列的值取决于其他列的值。CASE WHEN可以用于任何可以使用表达式的地方。 大致概…

JavaScript模块化:CommonJS、AMD与ES模块化

JavaScript模块化&#xff1a;CommonJS、AMD与ES模块化 随着Web应用程序变得越来越复杂&#xff0c;JavaScript代码的组织和管理变得越来越重要。为了解决这个问题&#xff0c;JavaScript社区引入了模块化编程的概念。本文将详细介绍三种主要的JavaScript模块化规范&#xff1…

二叉树和数据结构

小红的完全二叉树构造 题目描述 小红想构造一个总共 n 个节点完全二叉树&#xff0c;该二叉树满足以下两个性质&#xff1a; 1. 所有节点的权值值为 1 ~ n 的一个排列。 2. 除了根节点以外&#xff0c;每个节点的权值和它父亲的权值的乘积为偶数。 请你帮小红构造出这个二叉树…

three.js捋文档的记录笔记(五):threejs引入

地址1&#xff1a;http://www.webgl3d.cn/pages/aac9ab/ 地址2&#xff1a;http://www.webgl3d.cn/Three.js/ &#xff08;作者&#xff1a;郭隆邦 该说不说 大佬真牛 整的教程通俗易懂还详细&#xff09; 本地静态服务器 vscode配置live-server插件 在官网案例里打开对应.ht…

K8S一 k8s基础知识及实战

一 K8S 概览 1.1 K8S 是什么&#xff1f; K8S官网文档&#xff1a;https://kubernetes.io/zh/docs/home/ K8S 是Kubernetes的全称&#xff0c;源于希腊语&#xff0c;意为“舵手”或“飞行员”&#xff0c;官方称其是&#xff1a;用于自动部署、扩展和管理“容器化&#xff08…

软考 系统架构设计师系列知识点之大数据设计理论与实践(5)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之大数据设计理论与实践&#xff08;4&#xff09; 所属章节&#xff1a; 第19章. 大数据架构设计理论与实践 第3节 Lambda架构 19.3.1 Lambda架构对大数据处理系统的理解 Lambda架构由Storm的作者Nathan Marz提出&…

目标检测与图像分类的区别(概念)

目标检测和图像分类是计算机视觉领域的两个重要任务&#xff0c;它们有一些关键的区别&#xff1a; 1、任务目标 图像分类&#xff1a;图像分类的任务是将输入的图像分为不同的类别&#xff0c;通常是预定义的类别集合。在这种任务中&#xff0c;算法的目标是确定图像中包含的…

伺服系统中电机磁极偏角自学习的实现方案

一、 电机磁极偏角自学习原理简述 要知道磁极偏角&#xff0c;首先要明确的是磁极角&#xff0c;在我个人的理解里磁极角就是park和Ipark变换里所需的电角度&#xff0c;我们的矢量控制方法是定磁链的&#xff0c;就是要保证两相同步旋转坐标系的Id轴和三相静止坐标系的A轴要重…

45---M.2 SSD电路设计

视频链接 M.2 SSD硬件电路设计01_哔哩哔哩_bilibili M.2 SSD电路设计 1、M.2简介 1.1、M.2基本介绍 M.2接口也叫NGFF&#xff0c;英文全称Next Generation Form Factor。M.2接口是为超极本&#xff08;Ultrabook&#xff09;量身定做的新一代接口标准&#xff0c;是Intel推…

nginx优化总结及系统内核优化

一、nginx优化 1. 性能优化&#xff1a; 需求内容实现IO多路复用use epoll;设置工作进程数worker_processes 与CPU数量相同或auto;工作进程静态绑核worker_cpu_affinity设置并发worker_connections worker_rlimit_nofile连接保持超时keepalive_timeout 服务器超时时间 [客户端…

2024年150道高频Java面试题(二十八)

55. 解释一下 Java 中的等待/通知机制。 Java中的等待/通知机制是线程间通信的一种方式&#xff0c;它通过Object类的三个方法来实现&#xff1a;wait(), wait(long timeout), 和 notify(), 以及 notifyAll()。 wait(): 当一个线程调用一个共享对象的 wait() 方法时&#xff…

Java实现二叉树(下)

1.前言 http://t.csdnimg.cn/lO4S7 在前文我们已经简单的讲解了二叉树的基本概念&#xff0c;本文将讲解具体的实现 2.基本功能的实现 2.1获取树中节点个数 public int size(TreeNode root){if(rootnull){return 0;}int retsize(root.left)size(root.right)1;return ret;}p…

蓝桥杯每日一题:奶牛回家(最短路径)

晚餐时间马上就到了&#xff0c;奶牛们还在各自的牧场中悠闲的散着步。 当农夫约翰摇动铃铛&#xff0c;这些牛就要赶回牛棚去吃晚餐。 在吃晚餐之前&#xff0c;所有奶牛都在自己的牧场之中&#xff0c;有些牧场中可能没有奶牛。 每个牧场都通过一条条道路连接到一个或多个…

【C++算法模板】KMP算法:字符串匹配算法

文章目录 0&#xff09;概述1&#xff09;求解next数组2&#xff09;求解匹配位置的核心函数3&#xff09;完整代码 0&#xff09;概述 在做模式串与文本串的匹配问题时&#xff0c;匹配失败时&#xff0c;如果每次都只向后递进一位&#xff0c;时间复杂度为 O ( n m ) O(nm…

314_C++_QT表格的撤销、恢复,可对多行、多item进行撤销、恢复操作

行–删除后的,撤销、恢复图示: 原图示 删除后 撤销操作 恢复操作 item修改后的撤销、恢复 原item 撤销修改 恢复修改 代码: --</

[开发日志系列]PDF图书在线系统20240415

20240414 Step1: 创建基础vueelment项目框架[耗时: 1h25min(8:45-10:10)] 检查node > 升级至最新 (考虑到时间问题,没有使用npm命令行执行,而是觉得删除重新下载最新版本) > > 配置vue3框架 ​ 取名:Online PDF Book System 遇到的报错: 第一报错: npm ERR! …

实时避障系统开启盲人独立出行新时代

作为一名资深记者&#xff0c;我始终关注并报道科技如何助力特殊群体克服生活挑战的创新实践。近期&#xff0c;我有幸深入了解了一款专为盲人设计的辅助应用叫做蝙蝠避障&#xff0c;它以实时避障系统为核心&#xff0c;为视障人士独自出行提供了强有力的支持&#xff0c;悄然…

服务器挖矿病毒解决ponscan,定时任务解决

服务器挖矿病毒解决ponscan&#xff0c;定时任务解决 挖矿病毒会隐藏chattr的操作权限&#xff0c;让我们无法删除病毒文件&#xff0c;杀掉病毒进程。所以要去下载chattr.c的文件&#xff0c;编译成a.out。然后再对原来的chattr文件的权限进行修改。然后覆盖掉它。 chattr.c …