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("适配器重新加载成功");
    }
}

六、总结

本文通过支付系统对接多渠道的场景,展示了适配器模式 + 工厂模式的组合使用,主要收获如下:

  1. 解决接口兼容问题:通过适配器封装不同第三方SDK的接口差异,客户端只需依赖统一的目标接口,无需关注具体适配逻辑;
  2. 符合开闭原则:新增支付渠道时,只需新增适配器类并配置映射关系,无需修改原有业务代码;
  3. 提高代码复用性:通过抽象适配器类抽取通用逻辑,避免重复编写参数校验、日志记录等代码;
  4. 降低耦合度:客户端与适配者解耦,适配逻辑集中在适配器类中,便于后续维护和调整;
  5. 增强扩展性:支持动态加载适配器和热更新,适配频繁变更的业务场景。

在实际开发中,适配器模式不仅适用于第三方接口对接,还可用于旧系统迁移、新旧接口兼容、多版本API适配等场景。结合工厂模式后,能更好地解决适配器的管理问题,是系统集成和接口兼容场景的首选方案之一。

posted @ 2025-12-17 22:24  夏尔_717  阅读(5)  评论(0)    收藏  举报