今天来学习一下适配器设计模式
适配器模式是一种对象结构型设计模式,它的作用是将一个类的接口转换成客户端所需要的另一个接口,从而使原本不兼容的那些类能够一起工作。适配器模式包含三个核心角色:
- 目标接口(Target):定义客户端要调用的接口;
- 适配器类(Adapter):将被适配者接口转换成客户端希望的接口,通常是实现了目标接口的一个类
- 被适配者(Adaptee):需要被转换成客户端需要的接口的类。
适配器模式可以通过类适配器和对象适配器两种方式实现:
- 类适配器:通过继承适配器类来实现适配器模式
- 对象适配器:通过组合被适配者和适配器对象来实现适配器模式
适配器模式适用于以下场景:
- 要使用的类的接口不符合要求,需要对其进行适配
- 需要统一接口,将多个类的接口统一为一个接口
- 需要重用已经存在的类,但其接口不符合要求。
示例:将一个形状绘图程序从使用一个正方形转变为使用一个圆形
定义目标接口Shape:
public interface Shape {
void draw();
}
定义一个被适配的类Square:
public class Square() {
public void drawSquare() {
System.out.println("Draw a square");
}
}
定义适配器类CircleAdapter【客户端直接生成该类的实例,从而调用被适配类】
public class CircleAdapter implements Shape {
public final Square square;
public CircleAdapter(Square square) {
this.square = square;
}
public void draw() {
square.draw();
}
}
客户端代码,我们使用CircleAdapter适配器来将方形适配成圆形
public class Client {
public static void main(String[] args) {
Square square = new Square();
Shape circleAdapter = new CircleAdapter(square);
circleAdapter.draw()
}
}
通过适配器模式,我们可以将Square类的原有接口适配成Shape的接口,从而可以被客户端正常调用。
Spring MVC源码中是如何应用适配器模式的?
Spring MVC框架的核心是DispatcherServlet,它充当了前端控制器(Front Controller)的角色,接收请求并将其分派给相应的处理程序(Controller)。DispatcherServlet使用HandlerAdapter来处理请求,并调用相应的Controller方法。HandlerAdapter就是适配器模式的典型实现。
HandlerAdapter将请求转换为处理程序(Controller)可以理解的形式,并将处理程序的输出转换为适当的视图(View)表示。它充当了DispatcherServlet和Controller之间的适配器。
HandlerAdapter接口【Target目标接口】定义了处理程序的适配器应该具有的方法,包括以下两个方法:
public interface HandlerAdapter {
// 用于判断HandlerAdapter是否支持某个处理程序(Controller)
boolean supports(Object handler);
// 用于实际处理请求
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
// 该方法已经过时了
@Deprecated
long getLastModified(HttpServletRequest request, Object handler);
}
在Spring MVC框架中,有多个HandlerAdapter的实现类【Adapter适配器】,每个实现类处理不同类型的Controller。例如,RequestMappingHandlerAdapter用于处理使用@RequestMapping注解的Controller方法,SimpleControllerHandlerAdapter用于处理实现了Controller接口的Controller类。
1)找到入口
DispatcherServlet → doDispatch()
...
// mappedHandler.getHandler()可以理解为得到controller
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 请求方法类型
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 真正的执行Controller中的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
- 上面
mappedHandler.getHandler()可以理解为广义上的的controller(包括HttpRequestHandler,Servlet等),mappedHandler就是handlermapping返回的HandlerExecutionChain调用链,包括了handler和intercepters,这里可以看做客户端执行调用
接下来看一下getHandlerAdapter()
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
// 调用各个实现类的supports方法,查看是否符合对应适配器的要求
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
handlerAdapters是一个集合,里面存放了所有的HandlerAdapter实现类
2)举例:RequestMappingHandlerAdapter
接下来我们看一下RequestMappingHandlerAdapter,它继承了AbstractHandlerMethodAdapter,而AbstractHandlerMethodAdapter实现了HandlerAdapter接口,因此supports方法是在AbstractHandlerMethodAdapter中实现的,而下面提到的supportsInternal()是在RequestMappingHandlerAdapter中实现的
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
- supports方法首先判断处理程序是否为HandlerMethod类型,如果是,则调用supportsInternal方法继续判断。supportsInternal方法默认返回true,表示该适配器支持所有类型的HandlerMethod。
接下来,我们来看一下RequestMappingHandlerAdapter的handle方法,实现处理请求的逻辑:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// 利用HandlerMethodArgumentResolver解析Controller方法参数
Object[] args = getMethodArgumentValues(request, response, handlerMethod);
// 调用Controller方法并获取返回值
Object returnValue = invokeHandlerMethod(request, response, handlerMethod, args);
// 利用HandlerMethodReturnValueHandler处理Controller方法返回值
mav = getModelAndView(request, response, returnValue);
if (this.redirectModelScenario && mav != null && !mav.hasView()) {
// 如果返回值是重定向,则将模型数据存储到FlashMap中
RequestMappingHandlerMapping.flashMapping(mav.getModel(), request, response);
}
return mav;
}
- handle方法首先将处理程序强制转换为HandlerMethod类型,然后调用handleInternal方法继续处理。
- handleInternal方法中,首先利用HandlerMethodArgumentResolver解析Controller方法的参数,然后调用Controller方法并获取返回值,最后利用HandlerMethodReturnValueHandler处理Controller方法的返回值,生成ModelAndView对象。
- 如果返回值是重定向,则将模型数据存储到FlashMap中。最终,返回ModelAndView对象。
在RequestMappingHandlerAdapter中,HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler都充当了适配器的角色,将请求和返回值转换为Controller方法可以理解的形式,并将Controller方法的输出转换为适当的视图表示。这就是适配器模式在Spring MVC框架中的应用。
在实际开发中,我们可以通过自定义HandlerAdapter的实现类来扩展Spring MVC框架的功能。例如,我们可以定义一个自定义的HandlerAdapter来处理自定义的Controller方法。只需要实现HandlerAdapter接口并重写supports和handle方法即可。