- 一、适配器模式 知识回顾
- 二、类适配器使用案例之拓展登陆业务
- 2.1 模拟登陆的旧系统
- 2.2 旧系统拓展【类适配器】
- 2.3 旧系统拓展【接口适配器】
- 三、适配器模式在Spring源码中的应用
- 四、SpringMVC中的适配器模式
- 五、总结
- 参考资料
适配器定义: 将一个类的接口转换成另一种目标接口,使得原本由于接口不兼容而不能一起工作的类能一起工作
适配器分类: 类结构型模式(耦合读较高) 和 对象结构型模式 两种
优点:
- 支持调用目标接口
- 复用已有的类,而且无需修改原有的代码就可以重用现有的适配器类
- 将目标类和适配器类解藕,解决了目标类和适配器类接口不一致的问题
- 符合开闭原则
缺点:
- 适配器编写过程需结合业务场景全面考虑,可能会增加系统的复杂性
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变乱
主要组成:
- 目标接口 Target,当前系统业务所需要的接口,可以接口也可以是抽象类
- 适配者类 Adaptee,被访问和适配的现存组件库中的组件接口
- 适配器类 Adapter,相当于转换器,通过继承或引用 Adaptee ,把适配者接口转换成目标接口
模板代码:
第一种:类适配器模式(耦合度高)
// 1. 目标接口
interface Target{
public void request();
}
// 2. 适配者接口
class Adaptee{
public void originRequest(){
System.out.println("适配者原始的request方法");
}
}
// 3. 类适配器
class ClassAdapter extends Adaptee implements Target{
@Override
public void request() {
originRequest();
}
}
public class Demo{
public static void main(String[] args) {
System.out.println("类适配器模式");
Target target = new ClassAdapter();
target.request();
}
}
运行结果
类适配器模式
适配者原始的request方法
第二种:对象适配器
// 1. 目标接口
interface Target{
public void request();
}
// 2. 适配者接口
class Adaptee{
public void originRequest(){
System.out.println("适配者原始的request方法");
}
}
// 3. 对象适配器
class ObjectAdapter implements Target{
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee){
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.originRequest();
}
}
public class Demo{
public static void main(String[] args) {
System.out.println("对象适配器模式");
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
运行结果
对象适配器模式
适配者原始的request方法
适配器模式的使用场景:
- 以前开发的系统存在满足新功能需求的类,但其接口同新系统的接口不一致
- 使用第三方提供的组件,但接口定义和自己要求的接口定义不同
在使用 JavaWeb 开发时,几乎都离不开注册、登陆的业务,比如 CSDN,我们可以使用 账号密码登陆,也可以使用微信登陆、手机号登陆等,虽然登陆的形式不同,但其实登陆后的逻辑是相同的,都是将登陆状态保存到 Token 或者 Session。
接下来我们就通过一个实例来体会类适配器的用法,以一个旧版的登陆业务为例:
2.1 模拟登陆的旧系统- 前后端交互类 R.java
package com.uni.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public // 后端返回给前端的对象 class R{ private int code; private String msg; private T data; }
- 用户实体类 User.java
package com.uni.entity;
import lombok.Data;
@Data
public class User {
private String username;
private String password;
}
- 用户注册、登陆业务逻辑层 UserService.java (接口省略了)
package com.uni;
import com.uni.entity.R;
import com.uni.entity.User;
public class UserService {
// 注册方法
public R register(User user){
return new R(200, "注册成功", new User());
}
// 登录方法
public R login(User user){
return new R (200, "登陆成功", user);
}
}
上面旧系统中的三个方法是能实现用户注册与登陆的稳定代码(仅作示例,细节忽略)
2.2 旧系统拓展【类适配器】接下来我们使用类适配器模式实现不修改原有的代码来进行功能的拓展,比如需要实现 QQ登陆、微信登陆、手机号登陆。(假设拓展后的系统为 V1版本)
第一步,编写目标接口,即拓展的业务接口
public interface IUserServiceV1{
R loginForQQ(String api);
R loginForWechat(String api);
R loginForTelphone(String phone, String code);
}
第二步,增加适配者类,继承原有的逻辑,并实现上方的目标接口
package service.impl;
import com.uni.entity.R;
import com.uni.entity.User;
import service.IUserServiceV1;
import service.UserService;
public class UserServiceV1Adapter extends UserService implements IUserServiceV1 {
@Override
public R loginForQQ(String api) {
User user = new User();
// 根据 api 获取用户对象...
return login(user);
}
@Override
public R loginForWechat(String api) {
User user = new User();
// 根据 api 获取用户对象...
return login(user);
}
@Override
public R loginForTelphone(String phone, String code) {
User user = new User();
// 验证手机号, 并获取用户对象
return login(user);
}
public R login(User user){
return super.login(user);
}
}
测试:
public class Demo{
public static void main(String[] args) {
IUserServiceV1 service = new UserServiceV1Adapter();
R info1 = service.loginForQQ("QQ登陆请求成功的api");
R info2 = service.loginForTelphone("登陆的手机号", "接收到的验证码");
System.out.println("QQ登录: " + info1);
System.out.println("手机号登陆: " + info2);
}
}
2.3 旧系统拓展【接口适配器】
之前的拓展方式直接继承了原先的 UserService,可以看出耦合度比较高,现可以换一种方式,先定义一个适配器的接口,然后使用抽象类实现,最后不同登陆方式继承抽象类即可
第一步,定义适配器接口
package service;
import com.uni.entity.R;
import com.uni.entity.User;
public interface IUserServiceAdapter {
boolean support(Object adapter);
R login (Object loginInfo, Object adapter);
}
第二步,定义适配器抽象类
package service;
import com.uni.entity.R;
import com.uni.entity.User;
public abstract class AbstractAdapter extends UserService implements IUserServiceAdapter{
public R loginOrigin(User user) {
return super.login(user);
}
}
第三步,定义需要的适配器
QQ业务适配器类
package service;
import com.uni.entity.R;
import com.uni.entity.User;
public class UserServiceQQAdapter extends AbstractAdapter{
@Override
public boolean support(Object adapter) {
return adapter instanceof UserServiceQQAdapter;
}
@Override
public R login(Object loginInfo, Object adapter) {
if (!support(adapter))
return new R(500, "登陆失败", null);
// 根据 loginInfo 获取 User
User user = new User();
// 执行原始的登录
return super.loginOrigin(user);
}
}
微信业务适配器类
package service;
import com.uni.entity.R;
import com.uni.entity.User;
public class UserServiceWeiXinAdapter extends AbstractAdapter{
@Override
public boolean support(Object adapter) {
return adapter instanceof UserServiceWeiXinAdapter;
}
@Override
public R login(Object loginInfo, Object adapter) {
if(!support(adapter)){
return new R(500, "适配器不匹配", null);
}
// 根据微信登陆的 api 获取用户信息
User user = new User();
return super.loginOrigin(user);
}
}
测试:
import com.uni.entity.R;
import com.uni.entity.User;
import service.IUserServiceAdapter;
import service.UserServiceQQAdapter;
import service.UserServiceWeiXinAdapter;
public class Demo{
public static void main(String[] args) {
IUserServiceAdapter adapter1 = new UserServiceQQAdapter();
IUserServiceAdapter adapter2 = new UserServiceWeiXinAdapter();
R r1 = adapter1.login("QQ登陆的API", adapter1);
R r2 = adapter2.login("WX登陆的API", adapter2);
System.out.println("qq登陆结果: " +r1);
System.out.println("wx登陆结果: " + r2);
}
}
类图关系如下:
Spring版本 V5.3.21在Spring 的 AOP 面向切面编程 中,适配器模式运用的非常广泛。
Spring 提供了org.springframework.aop.framework.adapterDefaultAdvisorAdapterRegistry 类 专门用于注册不同的适配器
package org.springframework.aop.framework.adapter;
// 默认的通知适配器注册中心
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
// 存储 AdvisorAdapter 的列表,只有三个
private final List adapters = new ArrayList(3);
// 无参构造
public DefaultAdvisorAdapterRegistry() {
// 注册三种不同的 Advice 通知适配器
this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());
this.registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
// 获取 Advisor 接口
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException { }
// 获取 Advisor 接口所有的拦截器
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
}
// 注册 Adapter 适配器
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}
}
比如 Spring 支持使用 Advice(通知)来增强被代理类的功能,在Spring 5.3.21 版本中,Advice的类型主要有 BeforeAdvice、AfterAdvice、DynamicIntroductionAdvice 这三种,其中还有一些继承的子接口,如下图:
对于这么多的 Advice 接口,Spring 底层提供了不同的 Adapter 适配器对其进行管理。主要看 DefaultAdvisorAdapterRegistry类就知道了。
其内部使用 List 存储三种不同的 Advice 适配器类型,最终都存储为AdvisorAdapter 接口。Spring 会根据不同的 AOP 配置来确定使用对应的 Advice。
四、SpringMVC中的适配器模式适配器在 SpringMVC 中的经典使用体现在它的核心方法 doDispatch 中,接下来先以 HandlerAdapter 类为主:
这些适配调用的关键代码在 DispatcherServlet类的 doDispatch() 方法中:
doDispatch() 只在 doService() 方法中调用
package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 存储当前的请求
HttpServletRequest processedRequest = request;
// 初始化处理器
HandlerExecutionChain mappedHandler = null;
// 标记是否为文件处理的请求
boolean multipartRequestParsed = false;
// 获取同步请求的管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
// 初始化视图模型
ModelAndView mv = null;
// 初始化方法执行的异常
Exception dispatchException = null;
try {
// 检查是否为文件处理的请求
processedRequest = checkMultipart(request);
// 如果处理后的processes请求和之前不同,那么则标记为文件请求
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 获取当前请求对应的 Handler 处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) { // 处理器为空,跑出异常
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 获取当前请求对应的 Handler 适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 获取请求的方法
String method = request.getMethod();
// 判断是否为 GET 请求
boolean isGet = HttpMethod.GET.matches(method);
// 若为 GET 请求 且带有请求头
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;
}
// Actually invoke the handler.
// 调用真正的处理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 若已执行就返回, 防止重复执行
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 提交默认的视图名称
applyDefaultViewName(processedRequest, mv);
// 提交 POST的请求处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理请求的结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
// 提交结果
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
}
在上面 doDispatch () 方法的源码中,用到适配器的部分为:
// 根据 Handler 获取对应的适配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 调用适配器的 handle 方法处理请求 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
接下来再看看 getHandlerAdapter()方法的源码:
package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
@Nullable
private List handlerAdapters;
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");
}
}
通过源码可以看到,在 DispatcherServlet 类里定义了存储 Handler 适配器的列表,并提供了 getHandlerAdapter() 方法来获取 Handler 对应的适配器,判断适配器是否兼容则调用适配器接口提供的 supports 方法。
五、总结适配器模式支持在原有的系统上拓展接口,分为类适配器和对象/接口适配器两种。
适配器通常带有 Adapter 的标识。
在 Spring AOP 中使用到了适配器,不同的 Advice 通知接口有着不同的 AdviceAdapter 适配器接口,Spring 提供了 DefaultAdvisorAdapterRegistry 类来负责注册 AdvisorAdapter,
该类初始化就注册了三种不同的 AdviceAdapter 适配器实现类,分别是方法执行前、方法执行后返回前和抛出异常前的 AdviceAdatper。同时提供 wrap 方法,可以获取Advice 对应的接口。
在 SpringMVC 中,处理 Controller 层的方法就用到了 HandlerAdapter 适配器,Controller在请求后,会来到 DispatcherServlet类的 doService 方法 ,然后再调用 doDispatch 方法,在该方法中用到 适配器的方法其实就两个,第一个是获取对应的适配器 getHandlerAdapter,第二个就是调用适配器的 handle方法,根据 请求、响应 以及 Handler 来渲染视图模型。
参考资料[1] http://c.biancheng.net/view/1361.html



