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();  
    }  
      
}  
相关推荐
java1234_小锋5 小时前
Spring里AutoWired与Resource区别?
java·后端·spring
崎岖Qiu5 小时前
【深度剖析】:结合 Spring Bean 的生命周期理解 @PostConstruct 的原理
java·笔记·后端·spring·javaee
qq_12498707537 小时前
基于SpringBoot的闪电队篮球俱乐部管理系统的设计与开发(源码+论文+部署+安装)
java·数据库·spring boot·后端·spring·毕业设计·计算机毕业设计
茶本无香7 小时前
设计模式之八: 适配器模式解释及应用
java·设计模式·适配器模式
枫斗.7 小时前
Spring AI 自定义 ChatClient Bean 注入冲突问题详解
java·人工智能·spring
是三好7 小时前
javaSE
java·后端·spring
曹轲恒7 小时前
SpringBoot整合SpringMVC(下)
java·spring boot·spring
是三好8 小时前
Spring全家桶
java·后端·spring
空空kkk8 小时前
Spring、Spring MVC、SpringBoot的欢迎页配置
spring boot·spring·mvc
yangminlei8 小时前
Spring MVC 响应机制综合实践:页面、数据、JSON 与响应配置
spring·json·mvc