适配器模式(SpringMVC、线程源码分析)

适配器模式

适配器模式定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。——《设计模式:可复用面向对象软件的基础》

下图中是一个生活中的例子,标准的AC插头没办法直接使用欧式电源插座,通过交流电适配器,插头和插座之间就可以正常工作了。
再比如,生活中给手机充电,中国家用电的电压为220V,如果给手机充电直接使用220V的电压,手机就该爆炸了,所以充电时使用的充电器就承担了适配器的角色,将220V的电压转换成5V电压,使手机能正常充电。
在这里插入图片描述
适配器模式中主要有三种角色,适配器将被适配者中的方法适配为目标接口中的方法:

  • 目标:这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口
  • 适配者:已有接口,但是和客户器期待的接口不兼容
  • 适配器:将已有接口转换成目标接口

类适配器模式

适配器实现目标接口,继承适配者,适配器重写目标接口中的方法,在该方法内部可以调用所继承的适配者的方法来实现适配
在这里插入图片描述

举例:将220V电压适配成5V

适配者:已经存在的类,但能被新的需求直接使用,需要进行一定的转换

public class Voltage220V {

    public int output220V() {
        int src = 220;
        System.out.println("电压=" + src + "V");
        return src;
    }
}

目标接口:规定需要的接口类型

public interface IVoltage5V {
    int output5V();
}

适配器:实现目标接口,继承适配者

public class VoltageAdapter extends Voltage220V implements IVoltage5V {
    @Override
    public int output5V() {
        int srcV = output220V();
        int dstV = srcV/44;
        System.out.println("电压=" + dstV + "V");
        return dstV;
    }
}

测试:给手机充电

public class Phone {

    // 充电
    public void charging(IVoltage5V iVoltage5V) {
        if (iVoltage5V.output5V() == 5) {
            System.out.println("电压为5V可以充电");
        } else {
            System.out.println("电压为不是5V,不可以充电");
        }
    }
}

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

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

对象适配器模式

  1. 基本思路和类适配器模式相同,只是将适配器类做修改,不是继承适配者,而是持有适配者类的实例,以解决兼容性问题。即:聚合适配者,实现目标接口。
  2. 根据 “合成复用原则”,在系统中尽量使用关联关系来替代继承关系。
  3. 对象适配器模式是适配器模式常用的一种。

举例:将220V电压适配成5V(只是在适配器类处有所不同)

适配器:实现目标接口,组合/聚合适配者,这里采用聚合和组合的方式都可以,根据自己实际需要选择

public class VoltageAdapter implements IVoltage5V {
	private Voltage220V voltage220V = new Voltage220V();
    @Override
    public int output5V() {
        int srcV = output220V();
        int dstV = srcV/44;
        System.out.println("电压=" + dstV + "V");
        return dstV;
    }
}

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

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

缺省适配器模式(接口适配器模式)

当一个接口中有多个方法,但调用者并不想实现该接口的所以方法时,可以使用缺省适配器模式。

  1. 原理是,定义一个抽象类来默认实现接口中的所有方法,即:实现接口,重写接口中的所有方法,但不做具体实现
  2. 调用者通过继承抽象类来重写需要的方法

举例:缺省适配器

目标接口:目标接口中有多个方法,但我不想实现接口中的所有方法,只想实现其中一个或两个

public interface IVoltage5V {
    void m1();
    void m2();
    void m3();
    void m4();
}

适配器:是一个抽象类,不能被实例化,重写了接口的所有方法,但不做具体实现

public abstract class AbstractVoltage5V implements IVoltage5V {
    @Override
    public void m1() {

    }

    @Override
    public void m2() {

    }

    @Override
    public void m3() {

    }

    @Override
    public void m4() {

    }
}

测试:匿名内部类方式

public class Main {
    public static void main(String[] args) {
        IVoltage5V m1 = new AbstractVoltage5V() {
            @Override
            public void m1() {
                System.out.println("m1");
            }
        };
        m1.m1();
    }
}

hint:这里不能使用Lambda表达式,Lambda转换的目标类型必须是接口
在这里插入图片描述

适配器模式的源码级应用

在SpringMVC中的应用

在这里插入图片描述

DispatcherServlet的doDispatch方法是SpringMVC工作的主要方法,下面仅保留了MVC流程中关键的代码,并对此进行了注释说明

/**
     * 1、用户请求
     */
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        /**
         * 2-3、进入这个方法,遍历List<HandlerMapping>调用 HandlerMapping.getHandler(request) 返回 HandlerExecutionChain
         * 返回 HandlerExecutionChain 中包含Object类型的 handler对象(处理器对象),注意这里的处理器对象的类型是Object类型
         */
        mappedHandler = getHandler(processedRequest);

        /**
         * 4、进入这个方法,遍历 List<HandlerAdapter>,这里包含了预先加载的处理器适配器(处理器和处理器适配器是一一对应的,如HttpRequestHandler和HttpRequestHandlerAdapter)
         * HandlerAdapter接口定义了supports方法用于判断当前处理器适配器于传入的处理器对象的关系
         * 找到后返回当前处理器对象的处理器适配器
         */
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        /**
         * 执行拦截器链List<HandlerInterceptor>的preHandle前置处理
         */
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
        }

        /**
         * 5-7、通过处理器适配器的handle方法调用处理器对象的handleRequest方法,
         * 【疑问:适配器的作用在哪,为什么不直接调用处理器对象的handleRequest方法,反而加了一层处理器适配器,
         * 需要注意处理器对象的类型是Object类型,可以通过if-else来判断处理器对象的类型,但如果新增了一种处理器类,
         * 则需要在代码中加入if语句进行判断,这显然违反了开闭原则。
         * 而增加一层适配器就很好的解决了这个问题,新增一种处理器的同时也添加一个对应的处理器适配器,并存入List<HandlerMapping>集合中,
         * 在步骤4中即可得到对应的处理器适配】
         */
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


        /**
         * 为ModelAndView设置默认名
         */
        applyDefaultViewName(processedRequest, mv);

        /**
         * 执行拦截器链List<HandlerInterceptor>的postHandle后置处理
         */
        mappedHandler.applyPostHandle(processedRequest, response, mv);

        /**
         * 8-11、在该方法对ModelAndView进行视图解析,并调用render(mv, request, response);方法完成渲染视图并返回
         */
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }

确定当前请求的处理程序

@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

确定当前请求的处理程序适配器

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				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");
	}

查看HttpRequestHandlerAdapter适配器源代码

public class HttpRequestHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}

	@Override
	@SuppressWarnings("deprecation")
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}

}

可以看出,适配器和适配者间是一一对应的,HttpRequestHandlerAdapter 中已经默认指定了它的适配者是HttpRequestHandler

总结

这里使用到的适配器模式中,HandlerAdapter接口担任目标,HadnlerAdapter的实现类担任适配器,各种处理器担任适配者。

这里适配器模式的使用不是很明显,先看一下适配器模式的定义:适配器模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。

分析:

  1. 原本的处理器对象是Object类型,可以通过if-else来转换类型,但违背开闭原则,不符合客户预期的接口。
  2. 为满足客户需求,定义了一个HandlerAdapter接口,里面的方法是客户期待的方法。
  3. 创建具体处理器的适配器,实现HandlerAdapter接口,在方法参数中传入适配者(处理器对象)

在线程中的应用

创建线程的方式有三种:

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法
  3. 实现Callable接口,重写call方法
    java中是单继承,所以继承Thread会有一定的局限性,一般使用后两种。

FutureTask类有两中构造器:
在这里插入图片描述

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
}

public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
}

可以看到,FutureTask(Runnable runnable, V result)构造器内部调用Executors.callable方法,在该方法的内部返回了一个new RunnableAdapter<T>(task, result)RunnableAdapter就是一个适配器,将Runnable适配为Callable

static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

RunnableAdapterExecutors的一个静态内部类,其实现了Callable接口内部聚合了Runnable,将传入Runnable对象适配为Callable,这是典型的对象适配器模式

是的,Spring MVC框架中使用了适配器模式适配器模式的作用是将不同类型的处理器适配到统一的接口上,使得框架能够统一处理不同类型的处理器。在Spring MVC中,HandlerAdapter就是一个适配器,它负责将不同类型的处理器适配到统一的Controller接口上,使得框架能够统一调用Controller的方法。 使用适配器模式的原因是为了解决不同类型的处理器调用方式的不确定性。如果直接调用Controller方法,就需要使用if-else语句来判断处理器的类型,然后执行相应的方法。这样的实现方式不仅不灵活,而且当需要扩展Controller时,就需要修改原来的代码,违背了开闭原则。 Spring MVC通过适配器模式来获取对应的Controller。它定义了一个适配接口,使得每一种Controller都有一种对应的适配器实现类。适配器代替Controller执行相应的方法。当需要扩展Controller时,只需要增加一个适配器类就可以完成Spring MVC的扩展。 以下是模拟代码实现适配器模式的示例: ```java // 定义Controller接口 public interface Controller { void handleRequest(); } // 定义具体的Controller实现类 public class UserController implements Controller { @Override public void handleRequest() { System.out.println("Handling user request"); } } // 定义适配器接口 public interface HandlerAdapter { boolean supports(Object handler); void handle(Object handler); } // 定义UserController的适配器实现类 public class UserControllerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return handler instanceof UserController; } @Override public void handle(Object handler) { UserController userController = (UserController) handler; userController.handleRequest(); } } // 定义DispatcherServlet类,用于调度请求 public class DispatcherServlet { private List<HandlerAdapter> handlerAdapters; public DispatcherServlet() { handlerAdapters = new ArrayList<>(); handlerAdapters.add(new UserControllerAdapter()); } public void doDispatch(Object handler) { for (HandlerAdapter adapter : handlerAdapters) { if (adapter.supports(handler)) { adapter.handle(handler); break; } } } } // 测试代码 public class Main { public static void main(String[] args) { UserController userController = new UserController(); DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.doDispatch(userController); } } ``` 以上代码演示了Spring MVC中适配器模式的实现。DispatcherServlet负责调度请求,根据不同的处理器类型选择对应的适配器来处理请求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值