【适配器模式】Spring MVC中到底是如何应用适配器模式的?

751 阅读5分钟

今天来学习一下适配器设计模式

适配器模式是一种对象结构型设计模式,它的作用是将一个类的接口转换成客户端所需要的另一个接口,从而使原本不兼容的那些类能够一起工作。适配器模式包含三个核心角色:

  • 目标接口(Target):定义客户端要调用的接口
  • 适配器类(Adapter):将被适配者接口转换成客户端希望的接口,通常是实现了目标接口的一个类
  • 被适配者(Adaptee):需要被转换成客户端需要的接口的类。

适配器模式可以通过类适配器和对象适配器两种方式实现:

  • 类适配器:通过继承适配器类来实现适配器模式
  • 对象适配器:通过组合被适配者和适配器对象来实现适配器模式

适配器模式适用于以下场景:

  1. 要使用的类的接口不符合要求,需要对其进行适配
  2. 需要统一接口,将多个类的接口统一为一个接口
  3. 需要重用已经存在的类,但其接口不符合要求。

示例:将一个形状绘图程序从使用一个正方形转变为使用一个圆形

定义目标接口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类。

image-20230424104918104.png

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(包括HttpRequestHandlerServlet等),mappedHandler就是handlermapping返回的HandlerExecutionChain调用链,包括了handlerintercepters ,这里可以看做客户端执行调用

接下来看一下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方法即可。