文章目录
- [Spring MVC 中的适配器模式](#Spring MVC 中的适配器模式)
-
- 为什么不直接调用?
- [解决方案一:统一 Controller](#解决方案一:统一 Controller)
- 解决方案二:使用适配器模式
- [DispatcherServlet 对 Adpater 的使用](#DispatcherServlet 对 Adpater 的使用)
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 点需要注意的:
-
Adapter 和 Controller 总是成对出现的。也就是说,Controller 的作者写 Controller 时『放飞自我』的代价就是,他要『多』写个 Adapter 。当然,由于 Adapter 可能会具有通用性,因此,Controller 类和 Adapter 类的数量关系也不至于是 1 : 1 的关系。
-
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();
}
}