适配器模式以及在 SpringMVC 中的应用

前文

SpringMVC简介、入门

SpringMVC(高级参数、List类型绑定、@RequestMapping、限定请求方法、以及Controller方法返回值)

SpringMVC 常用注解解析

文章目录

适配器模式

基本介绍

  • 适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,主要的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作,其别名为包装器(Wrapper)
  • 适配器模式属于结构型模式
  • 主要分为三类:类型适配器模式、对象适配器模式、接口适配器模式

适配器模式工作原理

  • 适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容
  • 从用户的角度看不到被适配者,是解耦的
  • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口
  • 用户收到反馈结果,感觉只是和目标接口交互

类适配器模式实现

package com.java.springtest.classadapter;

// 被适配的类
public class Voltage {
    // 输出 220V 的电压
    public int outPut220V() {
        int src = 220;
        System.out.println("电压=" + src + "V");
        return src;
    }
}
package com.java.springtest.classadapter;

/**
 * @author Woo_home
 * @create by 2020/2/17
 */

// 适配接口
public interface IVoltage {
    int outPut();
}
package com.java.springtest.classadapter;

public class VoltageAdapter extends Voltage implements IVoltage {
    @Override
    public int outPut() {
        // 获取到 220V 电压
        int srcV = outPut220V();
        int dstV = srcV / 44;
        return dstV;
    }
}
package com.java.springtest.classadapter;

public class Phone {
    // 充电
    public void changing(IVoltage iVoltage) {
        if (iVoltage.outPut() == 5) {
            System.out.println("电压为 5V,可以充电");
        } else if (iVoltage.outPut() > 5) {
            System.out.println("电压大于 5V,不能充电");
        }
    }
}
package com.java.springtest.classadapter;

public class Client {
    public static void main(String[] args) {
        System.out.println("----------- 类适配器模式 ------------");
        Phone phone = new Phone();
        phone.changing(new VoltageAdapter());
    }
}

输出:
适配器模式以及在 SpringMVC 中的应用

类适配器模式的注意事项和细节

  • Java 是单继承机制,所以类适配器需要继承 src 类这一点算是一个缺点,因为这要求 dst 必须是接口,有一定的局限性
  • src 类的方法在 Adapter 中都会暴露出来,也增加了使用的成本
  • 由于其继承了 src 类,所以它可以根据需求重写 src 类的方法,使得 Adapter 的灵活性增强了

对象适配器模式实现

package com.java.springtest.classadapter;

// 被适配的类
public class Voltage {
    // 输出 220V 的电压
    public int outPut220V() {
        int src = 220;
        System.out.println("电压=" + src + "V");
        return src;
    }
}
package com.java.springtest.classadapter;

// 适配接口
public interface IVoltage {
    int outPut();
}
package com.java.springtest.classadapter;

public class VoltageAdapter implements IVoltage {

    private Voltage voltage;

    public VoltageAdapter(Voltage voltage) {
        this.voltage = voltage;
    }

    @Override
    public int outPut() {
        int dst = 0;
        if (null != voltage) {
            int src = voltage.outPut220V();// 获取 220V 电压
            dst  = src / 44;
        }
        return dst;
    }
}
package com.java.springtest.classadapter;

public class Phone {
    // 充电
    public void changing(IVoltage iVoltage) {
        if (iVoltage.outPut() == 5) {
            System.out.println("电压为 5V,可以充电");
        } else if (iVoltage.outPut() > 5) {
            System.out.println("电压大于 5V,不能充电");
        }
    }
}
package com.java.springtest.classadapter;

public class Client {
    public static void main(String[] args) {
        System.out.println("----------- 对象适配器模式 ------------");
        Phone phone = new Phone();
        phone.changing(new VoltageAdapter(new Voltage()));
    }
}

适配器模式以及在 SpringMVC 中的应用

对象适配器模式的注意事项和细节

  • 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同,根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst 必须是接口
  • 使用成本更低,更灵活

接口适配器模式

基本介绍

  • 一些书籍称为:适配器模式或缺省适配器模式
  • 当不需要全部实现接口提供的方法时,可以先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
  • 适用于一个接口不想使用其所有的方法的情况

适配器模式在 SpringMVC 框架中的应用与源码分析

  • SpringMVC 中的 HandlerAdapter 就是使用了适配器模式
  • SpringMVC 处理请求的流程
  • 使用 HandlerAdapter 的原因分析:可以看到处理器的类型不同,有多重实现方式,那么调用方式就是不确定的,如果需要直接调用 Controller 方法,需要调用的时候就得不断使用 if else 来进行判断是哪一种子类然后执行。那么如果后面扩展 Controller,则需要修改后面的代码,这样就违背了 OCP 原则

关于 SpringMVC 的介绍以及使用不会的可以在前文那里看下

以下代码位于 DispatchServlet 中的 doDispatch() 方法,通过 HandlerMapping 来映射 Controller
适配器模式以及在 SpringMVC 中的应用
定义控制器之后要获取这个控制器,也就是下面代码中的 mappedHandler.getHandler() ,getHandlerAdapter() 方法返回一个适配器。为什么要返回一个适配器呢?因为不同的 Handler 要用不同的适配器去调用相应的方法去进行处理
适配器模式以及在 SpringMVC 中的应用
接下来我们来看下 getHandlerAdapter() 这个方法是怎么跑的
适配器模式以及在 SpringMVC 中的应用
从代码中可以看到 getHandlerAdapter() 返回的是一个 HandlerAdapter,我们来看下 HandlerAdapter 是什么东西
适配器模式以及在 SpringMVC 中的应用
我们来看下有哪些类实现了这个接口
适配器模式以及在 SpringMVC 中的应用
接着我们继续看下 getHandlerAdapter() 这个方法是如何返回一个 HandlerAdapter 的

private List<HandlerAdapter> handlerAdapters;

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	// 判断 handlerAdapters 是否为空
    if (this.handlerAdapters != null) {
    	// 如果不为空,则遍历 handlerAdapters 
        Iterator var2 = this.handlerAdapters.iterator();

        while(var2.hasNext()) {
            HandlerAdapter adapter = (HandlerAdapter)var2.next();
            // 大家看过上面 HandlerAdapter 的源码之后会发现在 HandlerAdapter 有一个方法
            // 是 supports,而这里的方法就是 HandlerAdapter 中的
            // 这里是判断适配器处理器支不支持这个处理器
            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");
}

适配器模式以及在 SpringMVC 中的应用
而这个 handle 其实就是我们的 Controller,通过调用适配器调用 Controller 的方法并返回一个 ModelAndView(HandlerAdapter 接口中定义的 handle() 方法),也就是上述代码中的 mv

总结

HandlerAdapter 的实现子类使得每一种 Controller 有一种对应的适配器实现类,每种 Controller 有不同的实现方式

自己动手实现 SpringMVC 通过适配器设计模式获取到对应的 Controller 的源码

说明:

  • Spring 定义了一个适配器接口,使得每一种 Controller 都有一种对应的适配器实现类
  • 适配器替代 Controller 执行相应的方法
  • 扩展 Controller 时,只需要增加一个适配器类就完成了 SpringMVC 的扩展了

代码已上传至码云,需要的朋友可以去看下,自己动手实现 SpringMVC
适配器模式以及在 SpringMVC 中的应用

上一篇:SpingMVC


下一篇:spring MVC --spring MVC原理