一、适配器模式概述
1.1 什么是适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间进行协作。就像现实世界中的电源适配器可以将不同国家的插头标准进行转换一样,适配器模式在软件设计中起到了"转换器"的作用。
1.2 核心思想
- 解耦:将客户端与目标接口的实现分离
- 复用:让不兼容的类能够一起工作
- 透明:对客户端隐藏适配过程
1.3 适配器模式的三种形式
- 类适配器:通过继承实现
- 对象适配器:通过组合实现(更常用)
- 接口适配器:通过抽象类实现
二、适配器模式的结构
2.1 主要角色
java
// 目标接口(Target):客户端期望的接口
public interface Target {
void request();
}
// 源角色(Adaptee):需要适配的已有类
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specific request");
}
}
// 适配器(Adapter):实现目标接口,包装适配者
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); // 调用适配者的方法
}
}
三、Spring Boot中的适配器模式应用
3.1 Spring MVC中的HandlerAdapter
Spring MVC中,HandlerAdapter 是适配器模式的经典应用。它解决了不同类型的处理器(Controller)需要统一处理的问题。
java
// 示例:模拟Spring MVC的处理器适配
public interface Handler {
Object handle(HttpServletRequest request, HttpServletResponse response);
}
// 不同的处理器实现
@Component
public class AnnotationController implements Handler {
@Override
public Object handle(HttpServletRequest request, HttpServletResponse response) {
return "Annotation Controller Response";
}
}
@Component
public class SimpleController implements Handler {
@Override
public Object handle(HttpServletRequest request, HttpServletResponse response) {
return "Simple Controller Response";
}
}
// 适配器接口
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler);
}
// 具体适配器实现
@Component
public class AnnotationHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof AnnotationController;
}
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
AnnotationController controller = (AnnotationController) handler;
Object result = controller.handle(request, response);
return new ModelAndView("view", "data", result);
}
}
@Component
public class SimpleHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof SimpleController;
}
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
SimpleController controller = (SimpleController) handler;
Object result = controller.handle(request, response);
return new ModelAndView("view", "data", result);
}
}
3.2 Spring Security中的适配器应用
Spring Security使用适配器模式来支持不同的认证机制。
java
// 不同认证提供者
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication);
boolean supports(Class<?> authentication);
}
// Dao认证提供者
@Component
public class DaoAuthenticationProvider implements AuthenticationProvider {
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
UserDetails user = userDetailsService.loadUserByUsername(username);
if (passwordEncoder.matches(password, user.getPassword())) {
return new UsernamePasswordAuthenticationToken(
user, password, user.getAuthorities());
}
throw new BadCredentialsException("Invalid credentials");
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication);
}
}
// JWT认证提供者
@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
private final JwtTokenUtil jwtTokenUtil;
@Override
public Authentication authenticate(Authentication authentication) {
String token = (String) authentication.getCredentials();
if (jwtTokenUtil.validateToken(token)) {
String username = jwtTokenUtil.getUsernameFromToken(token);
List<GrantedAuthority> authorities = jwtTokenUtil.getAuthorities(token);
return new JwtAuthenticationToken(username, token, authorities);
}
throw new BadCredentialsException("Invalid JWT token");
}
@Override
public boolean supports(Class<?> authentication) {
return JwtAuthenticationToken.class
.isAssignableFrom(authentication);
}
}
四、实战示例:支付网关适配器
4.1 业务场景
假设我们需要集成多个第三方支付平台(支付宝、微信支付、PayPal),每个平台的接口都不相同。使用适配器模式可以统一支付接口。
4.2 代码实现
java
// 统一支付接口
public interface PaymentGateway {
PaymentResponse pay(PaymentRequest request);
PaymentResponse refund(RefundRequest request);
boolean supports(PaymentType type);
}
// 支付请求对象
@Data
public class PaymentRequest {
private String orderId;
private BigDecimal amount;
private String currency;
private PaymentType paymentType;
private Map<String, Object> additionalParams;
}
// 微信支付适配器
@Component
@Slf4j
public class WechatPayAdapter implements PaymentGateway {
private final WechatPayClient wechatPayClient;
@Override
public PaymentResponse pay(PaymentRequest request) {
try {
// 将统一请求适配为微信支付特定请求
WechatPayRequest wechatRequest = convertToWechatRequest(request);
// 调用微信支付SDK
WechatPayResponse response = wechatPayClient.unifiedOrder(wechatRequest);
// 将微信响应适配为统一响应
return convertToPaymentResponse(response);
} catch (Exception e) {
log.error("Wechat pay failed", e);
return PaymentResponse.failed(e.getMessage());
}
}
private WechatPayRequest convertToWechatRequest(PaymentRequest request) {
WechatPayRequest wechatRequest = new WechatPayRequest();
wechatRequest.setOutTradeNo(request.getOrderId());
wechatRequest.setTotalFee(request.getAmount().multiply(BigDecimal.valueOf(100)).intValue());
// ... 其他参数转换
return wechatRequest;
}
@Override
public boolean supports(PaymentType type) {
return PaymentType.WECHAT_PAY.equals(type);
}
}
// 支付宝适配器
@Component
@Slf4j
public class AlipayAdapter implements PaymentGateway {
private final AlipayClient alipayClient;
@Override
public PaymentResponse pay(PaymentRequest request) {
try {
// 将统一请求适配为支付宝特定请求
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", request.getOrderId());
bizContent.put("total_amount", request.getAmount().toString());
bizContent.put("subject", "订单支付");
// ... 其他参数
alipayRequest.setBizContent(bizContent.toString());
// 调用支付宝SDK
AlipayTradePagePayResponse response = alipayClient.pageExecute(alipayRequest);
// 将支付宝响应适配为统一响应
return convertToPaymentResponse(response);
} catch (Exception e) {
log.error("Alipay failed", e);
return PaymentResponse.failed(e.getMessage());
}
}
@Override
public boolean supports(PaymentType type) {
return PaymentType.ALIPAY.equals(type);
}
}
// 支付网关工厂
@Component
public class PaymentGatewayFactory {
private final List<PaymentGateway> gateways;
@Autowired
public PaymentGatewayFactory(List<PaymentGateway> gateways) {
this.gateways = gateways;
}
public PaymentGateway getGateway(PaymentType type) {
return gateways.stream()
.filter(gateway -> gateway.supports(type))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"Unsupported payment type: " + type));
}
}
// 支付服务
@Service
@Transactional
@Slf4j
public class PaymentService {
private final PaymentGatewayFactory gatewayFactory;
private final OrderService orderService;
public PaymentResponse processPayment(PaymentRequest request) {
try {
// 获取对应的支付网关
PaymentGateway gateway = gatewayFactory.getGateway(request.getPaymentType());
// 执行支付
PaymentResponse response = gateway.pay(request);
// 更新订单状态
if (response.isSuccess()) {
orderService.updateOrderStatus(request.getOrderId(),
OrderStatus.PAID,
response.getTransactionId());
}
return response;
} catch (Exception e) {
log.error("Payment processing failed", e);
return PaymentResponse.failed("Payment failed: " + e.getMessage());
}
}
}
4.3 配置文件
yaml
# application.yml
payment:
gateways:
alipay:
app-id: your-app-id
merchant-private-key: your-private-key
alipay-public-key: alipay-public-key
gateway-url: https://openapi.alipay.com/gateway.do
wechat:
app-id: wx-your-app-id
mch-id: your-mch-id
api-key: your-api-key
notify-url: https://your-domain.com/notify/wechat
五、适配器模式的最佳实践
5.1 何时使用适配器模式
- 集成遗留系统:当需要与老旧系统集成时
- 统一多个接口:多个相似功能但接口不同的类需要统一处理
- 第三方库集成:集成第三方SDK,但不想让SDK接口污染业务代码
- 接口版本兼容:新旧接口版本需要同时支持
5.2 Spring Boot中的最佳实践
- 使用@ConditionalOnProperty:根据配置动态创建适配器
java
@Configuration
@ConditionalOnProperty(name = "payment.gateway.alipay.enabled", havingValue = "true")
public class AlipayConfig {
@Bean
public PaymentGateway alipayAdapter() {
return new AlipayAdapter();
}
}
- 结合策略模式:适配器模式常与策略模式结合使用
java
@Service
public class PaymentStrategy {
private final Map<PaymentType, PaymentGateway> strategies;
@Autowired
public PaymentStrategy(List<PaymentGateway> gateways) {
strategies = gateways.stream()
.collect(Collectors.toMap(
gateway -> {
if (gateway.supports(PaymentType.ALIPAY)) return PaymentType.ALIPAY;
if (gateway.supports(PaymentType.WECHAT_PAY)) return PaymentType.WECHAT_PAY;
return PaymentType.UNKNOWN;
},
Function.identity()
));
}
public PaymentGateway getStrategy(PaymentType type) {
return strategies.get(type);
}
}
- 异常处理适配器
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(PaymentException.class)
public ResponseEntity<ApiResponse> handlePaymentException(
PaymentException ex,
WebRequest request) {
ApiResponse response = ApiResponse.error(
ex.getErrorCode(),
ex.getMessage()
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
@ExceptionHandler(ThirdPartyException.class)
public ResponseEntity<ApiResponse> handleThirdPartyException(
ThirdPartyException ex,
WebRequest request) {
// 将第三方异常适配为统一异常格式
PaymentException adaptedEx = adaptThirdPartyException(ex);
return handlePaymentException(adaptedEx, request);
}
private PaymentException adaptThirdPartyException(ThirdPartyException ex) {
String errorCode = mapErrorCode(ex.getOriginalCode());
String message = "Payment failed: " + ex.getMessage();
return new PaymentException(errorCode, message);
}
}
六、适配器模式的优缺点
6.1 优点
- 单一职责原则:将接口转换逻辑与业务逻辑分离
- 开闭原则:新增适配器无需修改现有代码
- 提高复用性:可以让多个不兼容的类一起工作
- 提高灵活性:可以动态切换不同的适配器
6.2 缺点
- 增加复杂性:引入了额外的类和接口
- 可能过度设计:如果接口本来就兼容,不需要使用适配器
- 调试困难:请求需要经过多层转发,调试链路较长
七、总结
适配器模式在Spring Boot中有着广泛的应用,它帮助我们在保持系统架构整洁的同时,优雅地处理不同组件之间的接口差异。通过合理的适配器设计,我们可以:
- 轻松集成各种第三方服务
- 保持核心业务代码的稳定性
- 提高系统的可扩展性和维护性
- 实现平滑的系统迁移和升级
在实际开发中,适配器模式通常与其他模式(如工厂模式、策略模式)结合使用,可以构建出更加灵活和健壮的系统架构。Spring Boot的自动配置和依赖注入机制,为适配器模式的实现提供了极大的便利,使得我们可以更加专注于业务逻辑的实现。