Spring MVC 中的适配器模式

文章目录

Spring MVC 中的适配器模式

为什么不直接调用?

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

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

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

java 复制代码
// 假设 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 框架。

java 复制代码
public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(
            HttpServletRequest req, 
            HttpServletResponse resp) 
            throws Exception {
        ...

        return mav;
    }
}

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

java 复制代码
// 假设 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 的接口要求:

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

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

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

java 复制代码
// 适用于 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

java 复制代码
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();  
    }  
      
}  
相关推荐
无心水2 小时前
5、微服务快速启航:基于Pig与BladeX构建高可用分布式系统实战
服务器·分布式·后端·spring·微服务·云原生·架构
求知摆渡2 小时前
Spring AI 多模型对话 Demo 实战:OpenAI/Ollama 一套接口、Redis 会话记忆、SSE 流式输出、AOP 日志打点
java·spring
hrhcode6 小时前
【Netty】三.ChannelPipeline与ChannelHandler责任链深度解析
java·后端·spring·springboot·netty
MX_93599 小时前
@Import整合第三方框架原理
java·开发语言·后端·spring
MX_935910 小时前
Spring注解方式整合Mybatis
java·后端·spring·mybatis
弹简特10 小时前
【JavaEE08-后端部分】SpringMVC03-SpringMVC第二大核心处理请求之Cookie/Session和获取header
java·spring boot·spring·java-ee
利刃大大11 小时前
【SpringCloud】远程调用OpenFeign && 快速入手 && 参数传递 && 继承方式 && 抽取方式 && 远程部署
后端·spring·spring cloud·openfeign·远程调用
努力也学不会java12 小时前
【Spring Cloud】统一服务入口-Gateway
后端·算法·spring·spring cloud·gateway·服务发现
梵得儿SHI1 天前
Spring Cloud 实战攻坚:企业级用户服务开发(注册登录 + JWT 认证 + 权限控制)
后端·spring·spring cloud·用户注册与登录·jwt无状态认证体系·rbac权限控制·微服务用户中心
玹外之音1 天前
Spring AI 实战:手把手教你构建支持多会话管理的智能聊天服务
java·spring