适配器模式——以springboot为例
什么是适配器模式?
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的对象能够相互合作。简单来说,适配器模式就像我们日常生活中的电源适配器,它能够连接两个原本不兼容的接口,使它们能够一起工作。
适配器模式的核心思想是:将一个类的接口转换成客户端所期望的另一个接口,使得原本因接口不兼容而无法一起工作的类能够协同工作。
适配器模式的结构
适配器模式主要包含以下角色:
- 目标接口(Target):目标接口,客户端所期望的接口
- 适配者(Adaptee):需要被适配的类或接口,但是与Target不兼容
- 适配器(Adapter):连接目标接口和适配者的中间件
- 用户(Client):需要适配器的对象
适配器模式有两种实现方式:
- 类适配器:使用继承的方式
- 对象适配器:使用组合的方式(更常用)
一个简单的适配器模式示例
假设我们有一个老系统中的 LegacyUser
类,但我们的新系统需要使用 User
接口。我们可以创建一个适配器来解决这个问题:
// 目标接口 (Target)
public interface User {String getName();String getEmail();String getPhone();
}// 适配者 (Adaptee) - 老系统中的类
public class LegacyUser {private String username;private String contact;private String telephone;// 构造函数、getter和setter省略public String getUsername() {return username;}public String getContact() {return contact;}public String getTelephone() {return telephone;}
}// 适配器 (Adapter)
public class LegacyUserAdapter implements User {private LegacyUser legacyUser;public LegacyUserAdapter(LegacyUser legacyUser) {this.legacyUser = legacyUser;}@Overridepublic String getName() {return legacyUser.getUsername();}@Overridepublic String getEmail() {return legacyUser.getContact();}@Overridepublic String getPhone() {return legacyUser.getTelephone();}
}// 客户端代码
public class Client {public static void main(String[] args) {// 创建一个老系统的用户LegacyUser oldUser = new LegacyUser();oldUser.setUsername("张三");oldUser.setContact("zhangsan@example.com");oldUser.setTelephone("13800138000");// 使用适配器将老系统用户适配到新系统User adaptedUser = new LegacyUserAdapter(oldUser);// 现在可以使用新系统的接口了System.out.println("姓名: " + adaptedUser.getName());System.out.println("邮箱: " + adaptedUser.getEmail());System.out.println("电话: " + adaptedUser.getPhone());}
}
Spring Boot 中的适配器模式
Spring Boot 框架中大量使用了适配器模式,下面我们来看几个典型的例子:
1. Spring MVC 中的 HandlerAdapter
在 Spring MVC 中,DispatcherServlet
是前端控制器,负责将请求分发给不同的处理器(Handler)。但是这些处理器的类型可能各不相同(如 Controller、HttpRequestHandler 等),它们的接口也不一致。
为了统一处理这些不同类型的处理器,Spring MVC 使用了适配器模式,引入了 HandlerAdapter
接口:
// 目标接口
public interface HandlerAdapter {boolean supports(Object handler);ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;long getLastModified(HttpServletRequest request, Object handler);
}
Spring MVC 为不同类型的处理器提供了不同的适配器实现:
// 适配器实现示例 - 为 @RequestMapping 注解的控制器提供适配
public class RequestMappingHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return handler instanceof HandlerMethod;}@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 处理请求的逻辑return handleInternal(request, response, (HandlerMethod) handler);}// 其他方法实现...
}
DispatcherServlet
的处理流程大致如下:
// DispatcherServlet 中的处理逻辑(简化版)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 找到处理请求的 handlerObject handler = getHandler(request);// 找到适合该 handler 的 HandlerAdapterHandlerAdapter ha = getHandlerAdapter(handler);// 使用适配器处理请求ModelAndView mv = ha.handle(request, response, handler);// 处理视图...
}// 查找合适的适配器
private HandlerAdapter getHandlerAdapter(Object handler) {for (HandlerAdapter ha : this.handlerAdapters) {if (ha.supports(handler)) {return ha;}}throw new ServletException("No adapter for handler [" + handler + "]");
}
通过这种方式,Spring MVC 可以支持多种类型的处理器,而 DispatcherServlet
不需要知道具体处理器的实现细节。
2. Spring Boot 中的 WebMvcConfigurer 适配器
在 Spring Boot 2.0 之前,我们通常通过继承 WebMvcConfigurerAdapter
来自定义 MVC 配置:
// Spring Boot 1.x 中的用法
public class WebConfig extends WebMvcConfigurerAdapter {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor());}
}
WebMvcConfigurerAdapter
就是一个适配器类,它实现了 WebMvcConfigurer
接口的所有方法(提供空实现),这样我们只需要重写需要的方法即可。
在 Java 8 之后,接口可以有默认方法,所以在 Spring Boot 2.0 中,WebMvcConfigurerAdapter
被废弃了,我们可以直接实现 WebMvcConfigurer
接口:
// Spring Boot 2.x 中的用法
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor());}
}
3. Spring Boot 中的 MessageConverter
Spring Boot 中的 HttpMessageConverter
也是适配器模式的一个很好的例子。它负责将 HTTP 请求体转换为 Java 对象,或将 Java 对象转换为 HTTP 响应体。
// 目标接口
public interface HttpMessageConverter<T> {boolean canRead(Class<?> clazz, MediaType mediaType);boolean canWrite(Class<?> clazz, MediaType mediaType);List<MediaType> getSupportedMediaTypes();T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;void write(T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}
Spring Boot 提供了多种 HttpMessageConverter
的实现,如 MappingJackson2HttpMessageConverter
(处理 JSON)、MarshallingHttpMessageConverter
(处理 XML)等。
实现一个自定义的 Spring Boot 适配器
让我们实现一个自定义的适配器,将第三方 API 的数据格式适配到我们的系统中:
假设我们有一个第三方天气 API,但它的数据格式与我们系统需要的格式不同:
// 第三方天气 API 提供的数据格式
public class ThirdPartyWeatherData {private String cityCode;private double temp;private int humidity;private String weatherCondition;// 构造函数、getter和setter省略
}// 我们系统中使用的天气数据格式
public interface WeatherInfo {String getCityName();double getTemperature();int getHumidity();String getCondition();boolean isRainy();
}// 适配器实现
@Service
public class WeatherAdapter implements WeatherInfo {private final ThirdPartyWeatherData thirdPartyData;private final CityService cityService; // 用于将城市代码转换为城市名称@Autowiredpublic WeatherAdapter(ThirdPartyWeatherService thirdPartyService, CityService cityService) {this.thirdPartyData = thirdPartyService.getWeatherData();this.cityService = cityService;}@Overridepublic String getCityName() {// 将城市代码转换为城市名称return cityService.getCityNameByCode(thirdPartyData.getCityCode());}@Overridepublic double getTemperature() {// 假设第三方API返回的是华氏度,我们需要转换为摄氏度return (thirdPartyData.getTemp() - 32) * 5 / 9;}@Overridepublic int getHumidity() {return thirdPartyData.getHumidity();}@Overridepublic String getCondition() {return thirdPartyData.getWeatherCondition();}@Overridepublic boolean isRainy() {// 根据天气状况判断是否下雨String condition = thirdPartyData.getWeatherCondition().toLowerCase();return condition.contains("rain") || condition.contains("drizzle");}
}// 控制器中使用适配器
@RestController
@RequestMapping("/weather")
public class WeatherController {private final WeatherInfo weatherInfo;@Autowiredpublic WeatherController(WeatherAdapter weatherAdapter) {this.weatherInfo = weatherAdapter;}@GetMapping("/current")public Map<String, Object> getCurrentWeather() {Map<String, Object> result = new HashMap<>();result.put("city", weatherInfo.getCityName());result.put("temperature", weatherInfo.getTemperature());result.put("humidity", weatherInfo.getHumidity());result.put("condition", weatherInfo.getCondition());result.put("isRainy", weatherInfo.isRainy());return result;}
}
适配器模式的优缺点
优点:
- 增加了类的透明性:通过适配器,客户端可以调用同一接口,无需关心底层实现
- 提高了类的复用性:适配器可以让原本不兼容的类一起工作
- 灵活性和扩展性好:可以在不修改原有代码的情况下,引入并使用新的组件
缺点:
- 增加了系统的复杂性:引入了新的类和接口
- 可能会导致代码可读性下降:如果过度使用适配器,可能会让系统变得难以理解
适配器模式的应用场景
- 需要使用一个已存在的类,但其接口不符合需求时
- 需要统一多个类的接口时
- 需要复用一些现有的类,但不能修改其源代码时
- 需要在新旧系统之间建立联系时
总结
适配器模式是一种非常实用的设计模式,它帮助我们解决接口不兼容的问题。在 Spring Boot 等框架中,适配器模式被广泛应用,使得框架能够灵活地支持各种不同的组件和实现。