SpringBoot使用设计模式一适配器模式
一、前言
在SpringBoot实际开发中,经常会遇到接口不兼容的场景:比如系统需要对接多个第三方服务(支付、短信、物流),每个服务提供的SDK接口格式、参数结构、返回值类型都各不相同,但核心业务逻辑(发起请求、接收响应、结果处理)却高度相似;又或者旧系统的旧接口需要被新系统复用,但接口定义与新系统的规范不匹配。
如果直接在业务代码中硬编码适配逻辑,会导致大量冗余代码和繁琐的if-else判断,后续新增或修改适配场景时,还需要修改原有业务逻辑,违背开闭原则。例如:
@Service
public class PaymentService {
// 微信支付SDK
private WeChatPaySDK weChatPaySDK;
// 支付宝SDK
private AliPaySDK aliPaySDK;
public String doPay(PaymentDTO dto) {
if ("WECHAT".equals(dto.getPayType())) {
// 微信支付适配:参数转换(元转分)、方法调用、结果封装
WeChatPayParam param = new WeChatPayParam();
param.setOrderId(dto.getOrderNo());
param.setTotalFee(dto.getAmount().multiply(new BigDecimal("100")).intValue());
WeChatPayResult result = weChatPaySDK.unifiedOrder(param);
return result.getCodeUrl();
} else if ("ALIPAY".equals(dto.getPayType())) {
// 支付宝支付适配:参数转换、方法调用、结果封装
AliPayParam param = new AliPayParam();
param.setOutTradeNo(dto.getOrderNo());
param.setTotalAmount(dto.getAmount().toString());
AliPayResult result = aliPaySDK.tradePagePay(param);
return result.getQrCode();
}
throw new IllegalArgumentException("不支持的支付类型");
}
}
针对这种接口不兼容但核心逻辑相通的场景,我们可以使用适配器模式来优化。
二、适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它的核心作用是将一个类的接口转换成客户端期望的另一个接口,使原本因接口不兼容而无法一起工作的类能够协同工作。
核心角色
- 目标接口(Target):客户端期望的统一接口,定义了业务所需的方法规范(如统一的支付接口、短信发送接口);
- 适配者(Adaptee):已存在的、接口不兼容但具备核心功能的类或接口(如第三方SDK、旧系统旧接口);
- 适配器(Adapter):实现目标接口,并持有适配者的引用,负责将目标接口的请求转换为适配者的接口调用,完成参数转换、方法映射、结果处理等适配逻辑。
使用场景
- 系统需要使用已存在的类,但该类的接口与系统要求的接口不兼容;
- 多个类功能相似,但接口定义不一致,需要统一接口供客户端调用;
- 需复用旧系统功能,但不想直接修改原有代码(遵循开闭原则);
- 对接多个第三方服务,需屏蔽不同服务的接口差异。
优点
- 解决接口兼容性问题,复用已有功能,避免重复开发;
- 解耦客户端与适配者,客户端只需依赖统一的目标接口,无需关注具体适配逻辑;
- 符合开闭原则,新增适配者时,只需新增对应的适配器类,无需修改原有代码;
- 提高代码灵活性和可维护性,适配逻辑集中在适配器类中,便于后续调整。
缺点
- 过多的适配器类可能导致系统复杂度增加;
- 适配器需理解目标接口和适配者的逻辑,增加了一定的编码成本;
- 若适配逻辑复杂,可能会降低系统的执行效率。
注意事项
- 适配器模式核心是 “适配” 而非 “修改”,不应在适配器中修改适配者的核心业务逻辑;
- 若多个适配器存在通用逻辑,可抽取抽象适配器类,减少代码冗余;
- 当接口差异过大时,需评估适配成本,必要时考虑重构接口而非强行适配。
三、实现案例
以SpringBoot支付系统对接微信支付、支付宝为例,使用适配器模式统一支付接口,屏蔽不同支付渠道的接口差异。
3.1 定义枚举与请求实体
枚举类
定义支付类型枚举,标识不同的支付渠道,方便后续适配器匹配:
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum PayTypeEnum {
WECHAT("WECHAT", "微信支付"),
ALIPAY("ALIPAY", "支付宝"),
UNION_PAY("UNION_PAY", "银联支付"),
;
private String code;
private String name;
// 根据编码获取枚举
public static PayTypeEnum getByCode(String code) {
for (PayTypeEnum type : values()) {
if (type.getCode().equals(code)) {
return type;
}
}
throw new IllegalArgumentException("不支持的支付类型:" + code);
}
}
统一请求实体
定义统一的支付请求和响应实体,封装客户端传入的通用参数(使用 @Valid 支持参数校验):
@ApiModel(value = "统一支付请求")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Validated
public class PaymentDTO implements Serializable {
@ApiModelProperty(value = "订单号", required = true)
@NotBlank(message = "订单号不能为空")
private String orderNo;
@ApiModelProperty(value = "支付金额(元)", required = true)
@NotNull(message = "支付金额不能为空")
@DecimalMin(value = "0.01", message = "支付金额不能小于0.01元")
private BigDecimal amount;
@ApiModelProperty(value = "支付类型(WECHAT/ALIPAY/UNION_PAY)", required = true)
@NotBlank(message = "支付类型不能为空")
private String payType;
@ApiModelProperty(value = "商品描述")
private String goodsDesc;
}
@ApiModel(value = "支付响应结果")
@Data
public class PaymentResponse implements Serializable {
private String payUrl; // 支付链接
private String qrCode; // 二维码
private String orderNo; // 订单号
private String payType; // 支付类型
}
3.2 定义目标接口(Target)
定义客户端期望的统一支付接口,规范所有支付渠道必须实现的方法:
public interface PaymentAdapter {
/**
* 支付类型(用于匹配对应的适配器)
*/
PayTypeEnum getPayType();
/**
* 发起支付(核心方法)
* @param paymentDTO 统一支付参数
* @return 统一支付响应
*/
R<PaymentResponse> doPay(PaymentDTO paymentDTO);
/**
* 查询支付结果
* @param orderNo 订单号
* @return 支付结果
*/
R<String> queryPayResult(String orderNo);
}
3.3 模拟适配者(Adaptee)
模拟微信支付、支付宝的SDK接口(实际开发中为第三方提供的现成SDK,接口格式不统一):
/**
* 微信支付SDK(适配者:接口格式与目标接口不兼容)
*/
public class WeChatPaySDK {
/**
* 微信统一下单接口(金额以分为单位,参数为微信自定义格式)
*/
public WeChatPayResult unifiedOrder(WeChatPayParam param) {
// 模拟SDK内部逻辑:调用微信支付接口
System.out.println("微信支付统一下单:订单号=" + param.getOrderId() + ",金额(分)=" + param.getAmount());
WeChatPayResult result = new WeChatPayResult();
result.setOrderId(param.getOrderId());
result.setCodeUrl("https://wechat.pay.com?orderId=" + param.getOrderId());
result.setSuccess(true);
return result;
}
/**
* 微信支付查询接口
*/
public WeChatQueryResult queryOrder(String orderId) {
System.out.println("微信支付查询:订单号=" + orderId);
WeChatQueryResult result = new WeChatQueryResult();
result.setOrderId(orderId);
result.setTradeStatus("SUCCESS"); // 支付成功状态
return result;
}
}
// 微信支付SDK相关参数和结果类
@Data
class WeChatPayParam {
private String orderId; // 订单号
private int amount; // 金额(分)
private String body; // 商品描述
}
@Data
class WeChatPayResult {
private String orderId;
private String codeUrl; // 支付链接
private boolean success;
}
@Data
class WeChatQueryResult {
private String orderId;
private String tradeStatus; // 支付状态
}
/**
* 支付宝SDK(适配者:接口格式与目标接口不兼容)
*/
public class AliPaySDK {
/**
* 支付宝电脑网站支付(金额以元为单位,参数为支付宝自定义格式)
*/
public AliPayResult tradePagePay(AliPayParam param) {
System.out.println("支付宝支付:商户订单号=" + param.getOutTradeNo()
+ ",金额(元)=" + param.getTotalAmount());
AliPayResult result = new AliPayResult();
result.setOutTradeNo(param.getOutTradeNo());
result.setQrCode("https://alipay.pay.com?outTradeNo=" + param.getOutTradeNo());
result.setCode("10000"); // 支付宝成功响应码
return result;
}
/**
* 支付宝支付查询接口
*/
public AliPayQueryResult queryOrder(String outTradeNo) {
System.out.println("支付宝查询:商户订单号=" + outTradeNo);
AliPayQueryResult result = new AliPayQueryResult();
result.setOutTradeNo(outTradeNo);
result.setTradeStatus("TRADE_SUCCESS"); // 支付成功状态
return result;
}
}
// 支付宝SDK相关参数和结果类
@Data
class AliPayParam {
private String outTradeNo; // 商户订单号
private String totalAmount; // 金额(元)
private String subject; // 商品描述
}
@Data
class AliPayResult {
private String outTradeNo;
private String qrCode; // 二维码
private String code; // 响应码
}
@Data
class AliPayQueryResult {
private String outTradeNo;
private String tradeStatus; // 支付状态
}
3.4 实现适配器(Adapter)
针对不同支付渠道,实现适配器类,适配目标接口与适配者接口,封装参数转换、方法调用、结果处理逻辑:
微信支付适配器
@Service
@Slf4j
public class WeChatPaymentAdapter implements PaymentAdapter {
// 持有适配者引用(微信支付SDK)
private final WeChatPaySDK weChatPaySDK = new WeChatPaySDK();
@Override
public PayTypeEnum getPayType() {
return PayTypeEnum.WECHAT;
}
@Override
public R<PaymentResponse> doPay(PaymentDTO paymentDTO) {
try {
// 1. 适配参数:将统一支付参数转换为微信SDK所需参数
WeChatPayParam weChatParam = new WeChatPayParam();
weChatParam.setOrderId(paymentDTO.getOrderNo());
// 微信支付金额以分为单位,需将元转换为分
weChatParam.setAmount(paymentDTO.getAmount().multiply(new BigDecimal("100")).intValue());
weChatParam.setBody(paymentDTO.getGoodsDesc());
// 2. 调用适配者(微信SDK)的核心方法
WeChatPayResult weChatResult = weChatPaySDK.unifiedOrder(weChatParam);
// 3. 适配结果:将微信SDK返回结果转换为统一响应格式
if (weChatResult.isSuccess()) {
PaymentResponse response = new PaymentResponse();
response.setOrderNo(weChatResult.getOrderId());
response.setPayType(getPayType().getCode());
response.setPayUrl(weChatResult.getCodeUrl());
return R.success(response, "微信支付发起成功");
}
return R.fail("微信支付发起失败");
} catch (Exception e) {
log.error("微信支付适配异常", e);
return R.fail("微信支付异常:" + e.getMessage());
}
}
@Override
public R<String> queryPayResult(String orderNo) {
WeChatQueryResult queryResult = weChatPaySDK.queryOrder(orderNo);
String status = "SUCCESS".equals(queryResult.getTradeStatus()) ? "支付成功" : "支付中";
return R.success(status, "订单号:" + orderNo);
}
}
支付宝支付适配器
@Service
@Slf4j
public class AliPaymentAdapter implements PaymentAdapter {
// 持有适配者引用(支付宝SDK)
private final AliPaySDK aliPaySDK = new AliPaySDK();
@Override
public PayTypeEnum getPayType() {
return PayTypeEnum.ALIPAY;
}
@Override
public R<PaymentResponse> doPay(PaymentDTO paymentDTO) {
try {
// 1. 适配参数:将统一支付参数转换为支付宝SDK所需参数
AliPayParam aliParam = new AliPayParam();
aliParam.setOutTradeNo(paymentDTO.getOrderNo());
aliParam.setTotalAmount(paymentDTO.getAmount().toString()); // 支付宝以元为单位
aliParam.setSubject(paymentDTO.getGoodsDesc());
// 2. 调用适配者(支付宝SDK)的核心方法
AliPayResult aliResult = aliPaySDK.tradePagePay(aliParam);
// 3. 适配结果:将支付宝SDK返回结果转换为统一响应格式
if ("10000".equals(aliResult.getCode())) {
PaymentResponse response = new PaymentResponse();
response.setOrderNo(aliResult.getOutTradeNo());
response.setPayType(getPayType().getCode());
response.setQrCode(aliResult.getQrCode());
return R.success(response, "支付宝支付发起成功");
}
return R.fail("支付宝支付发起失败");
} catch (Exception e) {
log.error("支付宝支付适配异常", e);
return R.fail("支付宝支付异常:" + e.getMessage());
}
}
@Override
public R<String> queryPayResult(String orderNo) {
AliPayQueryResult queryResult = aliPaySDK.queryOrder(orderNo);
String status = "TRADE_SUCCESS".equals(queryResult.getTradeStatus()) ? "支付成功" : "支付中";
return R.success(status, "订单号:" + orderNo);
}
}
3.5 控制器层调用(客户端)
客户端通过Spring自动注入所有适配器,根据支付类型匹配对应的适配器,无需关注具体适配逻辑:
@RestController
@RequestMapping("/payment")
@Slf4j
public class PaymentController {
@Autowired
private List<PaymentAdapter> paymentAdapterList;
/**
* 发起支付
*/
@PostMapping("/doPay")
public R<PaymentResponse> doPay(@RequestBody @Valid PaymentDTO paymentDTO) {
// 根据支付类型匹配对应的适配器
Optional<PaymentAdapter> adapterOptional = paymentAdapterList.stream()
.filter(adapter -> paymentDTO.getPayType().equals(adapter.getPayType().getCode()))
.findAny();
if (adapterOptional.isPresent()) {
return adapterOptional.get().doPay(paymentDTO);
}
return R.fail("不支持的支付类型:" + paymentDTO.getPayType());
}
/**
* 查询支付结果
*/
@GetMapping("/queryPayResult")
public R<String> queryPayResult(@RequestParam String orderNo, @RequestParam String payType) {
Optional<PaymentAdapter> adapterOptional = paymentAdapterList.stream()
.filter(adapter -> payType.equals(adapter.getPayType().getCode()))
.findAny();
if (adapterOptional.isPresent()) {
return adapterOptional.get().queryPayResult(orderNo);
}
return R.fail("不支持的支付类型:" + payType);
}
}
四、工厂模式管理适配器
为了优化适配器的获取逻辑,避免在控制器中重复编写流查询代码,可结合工厂模式封装适配器的注册与获取逻辑,提升代码复用性和可维护性。
4.1 使用@PostConstruct注解初始化
/**
* 支付适配器工厂(管理所有适配器)
*/
@Component
@Slf4j
public class PaymentAdapterFactory {
/**
* 缓存适配器:key=支付类型编码,value=对应的适配器(线程安全)
*/
private static final Map<String, PaymentAdapter> ADAPTER_MAP = new ConcurrentHashMap<>();
/**
* 注入所有PaymentAdapter的实现类(Spring自动扫描)
*/
@Resource
private List<PaymentAdapter> paymentAdapterList;
/**
* 初始化:将适配器注册到缓存(项目启动时执行)
*/
@PostConstruct
public void init() {
for (PaymentAdapter adapter : paymentAdapterList) {
String payTypeCode = adapter.getPayType().getCode();
ADAPTER_MAP.put(payTypeCode, adapter);
log.info("注册支付适配器:{} -> {}", payTypeCode, adapter.getClass().getSimpleName());
}
}
/**
* 根据支付类型获取适配器
* @param payType 支付类型编码
* @return 对应的适配器对象
*/
public static PaymentAdapter getAdapter(String payType) {
PaymentAdapter adapter = ADAPTER_MAP.get(payType);
if (adapter == null) {
throw new IllegalArgumentException("不支持的支付类型:" + payType);
}
return adapter;
}
}
4.2 改造控制器调用逻辑
@RestController
@RequestMapping("/payment")
@Slf4j
public class PaymentController {
@Autowired
private PaymentAdapterFactory adapterFactory;
/**
* 发起支付
*/
@PostMapping("/doPay")
public R<PaymentResponse> doPay(@RequestBody @Valid PaymentDTO paymentDTO) {
try {
PaymentAdapter adapter = adapterFactory.getAdapter(paymentDTO.getPayType());
return adapter.doPay(paymentDTO);
} catch (IllegalArgumentException e) {
return R.fail(e.getMessage());
}
}
/**
* 查询支付结果
*/
@GetMapping("/queryPayResult")
public R<String> queryPayResult(@RequestParam String orderNo, @RequestParam String payType) {
try {
PaymentAdapter adapter = adapterFactory.getAdapter(payType);
return adapter.queryPayResult(orderNo);
} catch (IllegalArgumentException e) {
return R.fail(e.getMessage());
}
}
}
4.3 其他初始化方式
除了@PostConstruct注解,还可通过以下方式实现适配器工厂的初始化,效果一致:
4.3.1 实现InitializingBean接口
@Component
@Slf4j
public class PaymentAdapterFactory implements InitializingBean {
private static final Map<String, PaymentAdapter> ADAPTER_MAP = new ConcurrentHashMap<>();
@Resource
private List<PaymentAdapter> paymentAdapterList;
@Override
public void afterPropertiesSet() throws Exception {
for (PaymentAdapter adapter : paymentAdapterList) {
String payTypeCode = adapter.getPayType().getCode();
ADAPTER_MAP.put(payTypeCode, adapter);
log.info("注册支付适配器:{} -> {}", payTypeCode, adapter.getClass().getSimpleName());
}
}
public static PaymentAdapter getAdapter(String payType) {
PaymentAdapter adapter = ADAPTER_MAP.get(payType);
if (adapter == null) {
throw new IllegalArgumentException("不支持的支付类型:" + payType);
}
return adapter;
}
}
4.3.2 使用构造方法注入
@Component
@Slf4j
public class PaymentAdapterFactory {
private static final Map<String, PaymentAdapter> ADAPTER_MAP = new ConcurrentHashMap<>();
// 构造方法注入所有适配器实现类
public PaymentAdapterFactory(List<PaymentAdapter> paymentAdapterList) {
for (PaymentAdapter adapter : paymentAdapterList) {
String payTypeCode = adapter.getPayType().getCode();
ADAPTER_MAP.put(payTypeCode, adapter);
log.info("注册支付适配器:{} -> {}", payTypeCode, adapter.getClass().getSimpleName());
}
}
public static PaymentAdapter getAdapter(String payType) {
PaymentAdapter adapter = ADAPTER_MAP.get(payType);
if (adapter == null) {
throw new IllegalArgumentException("不支持的支付类型:" + payType);
}
return adapter;
}
}
五、解决适配器类膨胀问题
如果系统需要对接的第三方服务越来越多(如新增银联支付、京东支付等),适配器类会不断膨胀,可通过以下方式优化:
5.1 抽取抽象适配器类
当多个适配器存在通用逻辑(如参数校验、日志记录、异常处理)时,可抽取抽象适配器类,封装通用逻辑,具体适配器只需实现差异化逻辑:
/**
* 抽象支付适配器(封装通用逻辑)
*/
@Slf4j
public abstract class AbstractPaymentAdapter implements PaymentAdapter {
/**
* 通用参数校验(所有适配器共用)
*/
protected void validateParams(PaymentDTO paymentDTO) {
log.info("执行支付参数校验:{}", paymentDTO.getOrderNo());
if (paymentDTO.getAmount().compareTo(new BigDecimal("100000")) > 0) {
throw new IllegalArgumentException("单笔支付金额不能超过10万元");
}
// 其他通用校验逻辑...
}
/**
* 通用日志记录(支付结果日志)
*/
protected void logPayResult(PaymentDTO paymentDTO, PaymentResponse response) {
log.info("支付发起成功:订单号={},支付类型={},支付链接={}",
response.getOrderNo(), response.getPayType(),
response.getPayUrl() != null ? response.getPayUrl() : response.getQrCode());
}
/**
* 抽象方法:子类实现具体支付逻辑(差异化部分)
*/
@Override
public abstract R<PaymentResponse> doPay(PaymentDTO paymentDTO);
/**
* 抽象方法:子类实现具体查询逻辑(差异化部分)
*/
@Override
public abstract R<String> queryPayResult(String orderNo);
}
改造微信支付适配器,只需实现差异化逻辑:
@Service
@Slf4j
public class WeChatPaymentAdapter extends AbstractPaymentAdapter {
private final WeChatPaySDK weChatPaySDK = new WeChatPaySDK();
@Override
public PayTypeEnum getPayType() {
return PayTypeEnum.WECHAT;
}
@Override
public R<PaymentResponse> doPay(PaymentDTO paymentDTO) {
try {
// 调用抽象类的通用校验逻辑
validateParams(paymentDTO);
// 差异化逻辑:参数转换、SDK调用、结果封装
WeChatPayParam weChatParam = new WeChatPayParam();
weChatParam.setOrderId(paymentDTO.getOrderNo());
weChatParam.setAmount(paymentDTO.getAmount().multiply(new BigDecimal("100")).intValue());
weChatParam.setBody(paymentDTO.getGoodsDesc());
WeChatPayResult weChatResult = weChatPaySDK.unifiedOrder(weChatParam);
if (weChatResult.isSuccess()) {
PaymentResponse response = new PaymentResponse();
response.setOrderNo(weChatResult.getOrderId());
response.setPayType(getPayType().getCode());
response.setPayUrl(weChatResult.getCodeUrl());
// 调用抽象类的通用日志逻辑
logPayResult(paymentDTO, response);
return R.success(response, "微信支付发起成功");
}
return R.fail("微信支付发起失败");
} catch (Exception e) {
log.error("微信支付适配异常", e);
return R.fail("微信支付异常:" + e.getMessage());
}
}
// 查询方法改造类似,省略...
}
5.2 使用注解标记适配器
自定义注解标记适配器对应的支付类型,简化工厂类中的注册逻辑,无需每个适配器都实现getPayType()方法:
/**
* 支付适配器注解(标记支付类型)
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PayAdapter {
String value(); // 支付类型编码
}
在适配器类上添加注解:
@Service
@Slf4j
@PayAdapter("WECHAT") // 标记支付类型
public class WeChatPaymentAdapter extends AbstractPaymentAdapter {
// 无需实现getPayType()方法
// ...其他逻辑不变
}
改造工厂类,通过注解获取支付类型并注册:
@Component
@Slf4j
public class PaymentAdapterFactory {
private static final Map<String, PaymentAdapter> ADAPTER_MAP = new ConcurrentHashMap<>();
@Resource
private List<PaymentAdapter> paymentAdapterList;
@PostConstruct
public void init() {
for (PaymentAdapter adapter : paymentAdapterList) {
// 通过注解获取支付类型编码
PayAdapter payAdapter = adapter.getClass().getAnnotation(PayAdapter.class);
if (payAdapter != null) {
String payTypeCode = payAdapter.value();
ADAPTER_MAP.put(payTypeCode, adapter);
log.info("注册支付适配器:{} -> {}", payTypeCode, adapter.getClass().getSimpleName());
}
}
}
public static PaymentAdapter getAdapter(String payType) {
PaymentAdapter adapter = ADAPTER_MAP.get(payType);
if (adapter == null) {
throw new IllegalArgumentException("不支持的支付类型:" + payType);
}
return adapter;
}
}
5.3 动态加载适配器(策略注册表)
将适配器的映射关系抽离到配置文件中,实现适配器的动态配置与热更新,无需修改代码即可新增或修改适配器:
5.3.1 定义配置实体与配置文件
在application.yml中配置支付类型与适配器类的映射关系:
# 支付适配器注册表配置
payment:
adapter:
registry:
WECHAT: com.example.adapter.WeChatPaymentAdapter
ALIPAY: com.example.adapter.AliPaymentAdapter
UNION_PAY: com.example.adapter.UnionPayPaymentAdapter
定义配置实体类,绑定配置:
@Data
@Component
@ConfigurationProperties(prefix = "payment.adapter")
public class PaymentAdapterProperties {
/**
* 适配器注册表:key=支付类型编码,value=适配器类全限定名
*/
private Map<String, String> registry;
}
5.3.2 改造工厂类(动态加载)
@Component
@Slf4j
public class PaymentAdapterFactory implements InitializingBean {
private static final Map<String, PaymentAdapter> ADAPTER_MAP = new ConcurrentHashMap<>();
@Autowired
private ApplicationContext applicationContext;
@Autowired
private PaymentAdapterProperties adapterProperties;
@Override
public void afterPropertiesSet() throws Exception {
reloadAdapters();
}
/**
* 重新加载适配器(支持热更新)
*/
public void reloadAdapters() {
Map<String, String> registry = adapterProperties.getRegistry();
if (registry == null || registry.isEmpty()) {
log.warn("支付适配器注册表为空");
return;
}
ADAPTER_MAP.clear();
for (Map.Entry<String, String> entry : registry.entrySet()) {
String payTypeCode = entry.getKey();
String className = entry.getValue();
try {
// 反射创建适配器实例(Spring管理的Bean)
Class<?> adapterClass = Class.forName(className);
PaymentAdapter adapter = (PaymentAdapter) applicationContext.getBean(adapterClass);
ADAPTER_MAP.put(payTypeCode, adapter);
log.info("加载适配器成功:{} -> {}", payTypeCode, className);
} catch (Exception e) {
log.error("加载适配器失败:{} -> {}", payTypeCode, className, e);
throw new RuntimeException("加载适配器失败:" + className, e);
}
}
}
public static PaymentAdapter getAdapter(String payType) {
PaymentAdapter adapter = ADAPTER_MAP.get(payType);
if (adapter == null) {
throw new IllegalArgumentException("不支持的支付类型:" + payType);
}
return adapter;
}
}
5.3.3 支持热更新(可选)
结合Spring Cloud Config或@RefreshScope实现配置热更新,修改配置文件后无需重启服务即可生效:
@Data
@Component
@RefreshScope
@ConfigurationProperties(prefix = "payment.adapter")
public class PaymentAdapterProperties {
private Map<String, String> registry;
}
// 新增刷新接口
@RestController
@RequestMapping("/adapter")
public class AdapterManagerController {
@Autowired
private PaymentAdapterFactory adapterFactory;
@PostMapping("/reload")
public R<?> reloadAdapters() {
adapterFactory.reloadAdapters();
return R.success("适配器重新加载成功");
}
}
六、总结
本文通过支付系统对接多渠道的场景,展示了适配器模式 + 工厂模式的组合使用,主要收获如下:
- 解决接口兼容问题:通过适配器封装不同第三方
SDK的接口差异,客户端只需依赖统一的目标接口,无需关注具体适配逻辑; - 符合开闭原则:新增支付渠道时,只需新增适配器类并配置映射关系,无需修改原有业务代码;
- 提高代码复用性:通过抽象适配器类抽取通用逻辑,避免重复编写参数校验、日志记录等代码;
- 降低耦合度:客户端与适配者解耦,适配逻辑集中在适配器类中,便于后续维护和调整;
- 增强扩展性:支持动态加载适配器和热更新,适配频繁变更的业务场景。
在实际开发中,适配器模式不仅适用于第三方接口对接,还可用于旧系统迁移、新旧接口兼容、多版本API适配等场景。结合工厂模式后,能更好地解决适配器的管理问题,是系统集成和接口兼容场景的首选方案之一。

浙公网安备 33010602011771号