SpringBoot使用设计模式一策略模式

一、前言

假如现在需要实现一个关于交易的功能,这个功能大致用到的参数都一样,但是在交易类型上分为充值、转账、消费、退款和提现等。不同的交易申请流程都属于交易功能的一部分,但是结合业务后,逻辑算法都是基本独立的。

按照往常的写法,可能会出现如下的代码写法:

public String createOrder(OrderInfo orderInfo) {
    if (orderInfo.getType().equals("deposit")) {            // 充值
        //...
    } else if (orderInfo.getType().equals("withdraw")) {    // 提现
        //...
    }
}

针对这种场景,我们可以使用策略模式可以。

二、策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法(策略),将每个算法封装成独立的类,使它们可以相互替换,且算法的变化不会影响使用算法的客户端。

策略模式包含三个核心角色:

  • 抽象策略(Strategy):定义策略的公共接口,规范策略的行为(如交易申请、确认支付);
  • 具体策略(Concrete Strategy):实现抽象策略接口,封装具体的业务逻辑(如充值策略、提现策略);
  • 上下文(Context):持有策略对象的引用,负责调用具体策略(本文中结合工厂模式实现上下文的功能)。

使用场景:

  1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  2. 一个系统需要动态地在几种算法中选择一种。
  3. 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

优点:

  1. 干掉繁琐的ifswitch判断逻辑;
  2. 代码优雅、可复用、可读性好;
  3. 符合开闭原则,扩展性好、便于维护;
  4. 符合开闭原则;

缺点:

  1. 策略如果很多的话,会造成策略类膨胀;
  2. 使用者必须清楚所有的策略类及其用途;

三、实现案例

请求实体类,充值、体现的实体省略。

3.1 定义枚举与请求实体

枚举类

@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum TransactionModeEnum {

    DEPOSIT("1", "充值"),
    WITHDRAW("2", "提现"),
    ;

    private String code;
    private String name;

    //根据编码获取枚举,方便后续转换
    public static TransactionModeEnum getByCode(String code) {
        for (TransactionModeEnum mode : values()) {
            if (mode.getCode().equals(code)) {
                return mode;
            }
        }
        throw new IllegalArgumentException("不支持的交易类型:" + code);
    }
}

定义交易请求实体,封装不同交易类型的参数(使用@Valid支持参数校验,增强健壮性):

@ApiModel(value = "发起交易申请")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TransactionApply implements Serializable {

    @ApiModelProperty(value = "交易类型")
    private Integer transactionMode;

    @ApiModelProperty(value = "充值")
    private DepositApply depositApply;

    @ApiModelProperty(value = "提现")
    private WithdrawApply withdrawApply;
}

3.2 定义抽象策略接口

定义交易策略的公共接口,规范所有交易类型必须实现的方法:

public interface TransactionService {

    /**
     * 交易类型
     *
     * @return
     */
    TransactionModeEnum transactionMode();

    /**
     * 交易申请
     *
     * @param transactionApply
     * @return
     */
    R<?> apply(TransactionApply transactionApply);

    /**
     * 确认支付(短信验证码确认)
     *
     * @param transactionVerify
     * @return
     */
    R<?> verify(TransactionVerify transactionVerify);
}

3.3 实现具体策略类

针对不同交易类型,实现具体的策略类,封装各自的业务逻辑:

@Service
@Slf4j
public class DepositApplyServiceImpl implements TransactionService {

    /**  
     * 交易类型
     *
     * @return
     */
    @Override
    public TransactionModeEnum transactionMode() {
        return TransactionModeEnum.DEPOSIT;
    }

    /**
     * 充值
     *
     * @param transactionApply
     * @return
     */
    @Override
    public R<?> apply(TransactionApply transactionApply) {
        //业务代码实现
    }
}

提现service实现类

@Service
@Slf4j
public class WithdrawApplyServiceImpl implements TransactionService {

    /**  
     * 交易类型
     *
     * @return
     */
    @Override
    public TransactionModeEnum transactionMode() {
        return TransactionModeEnum.WITHDRAW;
    }

    /**
     * 提现
     *
     * @param transactionApply
     * @return
     */
    @Override
    public R<?> apply(TransactionApply transactionApply) {
        //业务代码实现
    }
}

3.4 控制器层调用(客户端)

Controller调用

public class TransactionController {

    @Autowired
    private List<TransactionService> transactionList;
    
    /**
     * 交易申请
     */
    @PostMapping("/transactionApply")
    public R<?> transactionApply(@RequestBody TransactionApply transactionApply) {
        Optional<TransactionService> transactionOptional = transactionList.stream()
                   .filter(t -> transactionApply.getTransactionMode()
                            .equals(t.transactionMode().getCode())).findAny();
        if (transactionOptional.isPresent()) {
            return transactionOptional.get().apply(transactionApply);
        }
        return R.fail();
    }
}

四、工厂模式初始化策略

在上述工厂类中,我们使用了@PostConstruct注解完成策略对象的初始化注册,除此之外,Spring还提供了InitializingBean接口来实现相同的效果。这两种方式都能在Spring Bean初始化完成后执行自定义逻辑,下面详细讲解这两种方式的实现与区别。

4.1 使用构造方法

通过构造方法注入ListSpring会自动扫描所有实现类并注入。

@Component
@Slf4j
public class TransactionFactory {

    // 缓存策略:key=交易类型编码,value=策略对象(线程安全)
    private static final Map<String, TransactionService> TRANSACTION_MAP = new ConcurrentHashMap<>();

    // 构造方法注入所有策略实现类(Spring自动扫描所有TransactionStrategy子类)
    public TransactionFactory(List<TransactionService> transactionList) {
        // 初始化策略映射
        for (TransactionService transaction : transactionList) {
            String modeCode = transaction.getTransactionMode().getCode();
            TRANSACTION_MAP.put(modeCode, transaction);
            log.info("注册交易策略:类型={},策略类={}",
                    modeCode, transaction.getClass().getSimpleName());
        }
    }

    /**
     * 根据交易类型获取策略
     * @param modeCode 交易类型编码
     * @return 对应的策略对象
     */
    public static TransactionService getTransaction(String modeCode) {
        TransactionService transaction = TRANSACTION_MAP.get(modeCode);
        if (transaction == null) {
            throw new IllegalArgumentException("不支持的交易类型:" + modeCode);
        }
        return transaction ;
    }
}

核心特点:​

  • 代码简洁,无需手动初始化;​
  • 依赖注入完成后直接初始化,线程安全;​
  • 构造方法执行顺序早于@PostConstruct,避免空指针。

4.2 使用@PostConstruct注解

@PostConstructJSR-250规范中的注解,作用于Bean的方法上,当BeanSpring容器初始化(依赖注入完成后)时,会执行该注解标记的方法。

/**
 * 交易策略工厂(工厂模式)
 * 负责策略的注册与获取
 */
@Component
public class TransactionFactory {

    /**
     * 缓存策略对象:key=交易类型编码,value=对应的策略对象
     */
    private static final Map<String, TransactionService> TRANSACTION_MAP = new ConcurrentHashMap<>();

    /**
     * 注入所有TransactionService的实现类(Spring自动扫描)
     */
    @Resource
    private List<TransactionService> transactionList;

    /**
     * 初始化:将策略对象注册到MAP中(项目启动时执行)
     */
    @PostConstruct
    public void init() {
        for (TransactionService transaction : transactionList) {
            String code = transaction.getTransactionMode().getCode();
            TRANSACTION_MAP.put(code, transaction);
            log.info("注册交易策略:{} -> {}", code, transaction.getClass().getSimpleName());
        }
    }

    /**
     * 根据交易类型编码获取策略对象
     * @param transactionMode 交易类型编码
     * @return 对应的策略对象
     * @throws IllegalArgumentException 若策略不存在则抛出异常
     */
    public static TransactionService getTransaction(String transactionMode) {
        TransactionService transaction = TRANSACTION_MAP.get(transactionMode);
        if (transaction == null) {
            throw new IllegalArgumentException("不支持的交易类型:" + transactionMode);
        }
        return transaction;
    }
}

核心特点:

  • 注解式开发,代码简洁,耦合度低;
  • 方法名可自定义,无固定要求;
  • 执行顺序:构造方法 → 依赖注入(@Autowired/@Resource) → @PostConstruct标记的方法。

4.3 实现InitializingBean接口

InitializingBeanSpring提供的接口,包含一个afterPropertiesSet()方法,当Bean的属性设置完成(依赖注入完成)后,Spring会调用该方法。

改造后的工厂类代码:

/**
 * 交易策略工厂(实现InitializingBean接口完成初始化)
 */
@Component
@Slf4j
public class TransactionFactory implements InitializingBean {

    /**
     * 缓存策略对象:key=交易类型编码,value=对应的策略对象
     */
    private static final Map<String, TransactionService> TRANSACTION_MAP = new ConcurrentHashMap<>();

    /**
     * 注入所有TransactionService的实现类(Spring自动扫描)
     */
    @Resource
    private List<TransactionService> transactionList;

    /**
     * 实现InitializingBean的afterPropertiesSet方法,完成策略注册
     * 该方法在依赖注入完成后执行
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        for (TransactionService transaction : transactionList) {
            String code = transaction.getTransactionMode().getCode();
            TRANSACTION_MAP.put(code, transaction);
            log.info("注册交易策略:{} -> {}", code, transaction.getClass().getSimpleName());
        }
    }

    /**
     * 根据交易类型编码获取策略对象
     * @param transactionMode 交易类型编码
     * @return 对应的策略对象
     * @throws IllegalArgumentException 若策略不存在则抛出异常
     */
    public static TransactionService getTransaction(String transactionMode) {
        TransactionService transaction = TRANSACTION_MAP.get(transactionMode);
        if (transaction == null) {
            throw new IllegalArgumentException("不支持的交易类型:" + transactionMode);
        }
        return transaction;
    }
}

核心特点:

  • 基于接口实现,属于Spring的原生方式;
  • 方法名固定为afterPropertiesSet(),不可自定义;
  • 执行顺序:构造方法 → 依赖注入 → afterPropertiesSet()。

4.4 使用@Bean注解

除了上述两种方式,还可以在配置类中通过@Bean注解的initMethod属性指定初始化方法,效果与前两者一致,示例如下:

工厂类不再使用@Component注解让Spring自动扫描,而是通过配置类的@Bean注解手动创建,同时定义初始化方法

@Slf4j
public class TransactionFactory {

    /**
     * 缓存策略对象:key=交易类型编码,value=策略对象(线程安全)
     */
    private static final Map<String, TransactionService> TRANSACTION_MAP = new ConcurrentHashMap();

    /**
     * 策略列表(需通过构造方法手动传入,而非Spring自动注入)
     */
    private final List<TransactionService> transactionList;

    // 构造方法:接收策略列表参数(由配置类传入)
    public TransactionFactory(List<TransactionService> transactionList) {
        this.transactionList = transactionList;
    }

    /**
     * 初始化方法:注册所有策略(将被@Bean的initMethod指定调用)
     */
    public void initStrategy() {
        if (CollectionUtils.isEmpty(transactionList)) {
            log.warn("未获取到任何交易策略实现类,策略注册失败");
            return;
        }
        // 遍历策略列表,注册到Map缓存
        for (TransactionService transaction : transactionList) {
            String modeCode = transaction.transactionMode().getCode();
            TRANSACTION_MAP.put(modeCode, transaction);
            log.info("注册交易策略:编码={},策略类={}", 
                    modeCode, transaction.getClass().getSimpleName());
        }
    }

    /**
     * 根据交易类型编码获取策略对象
     * @param transactionMode 交易类型编码
     * @return 对应的策略对象
     * @throws IllegalArgumentException 若策略不存在则抛出异常
     */
    public static TransactionService getTransaction(String transactionMode) {
        TransactionService transaction = TRANSACTION_MAP.get(transactionMode);
        if (transaction == null) {
            throw new IllegalArgumentException("不支持的交易类型:" + transactionMode);
        }
        return transaction;
    }
}
@Configuration
public class TransactionConfig {

    /**
     * 注入所有TransactionService实现类(Spring自动扫描)
     */
    @Autowired
    private List<TransactionService> transactionList;

    /**
     * 手动注册TransactionFactory Bean,并指定初始化方法
     * @return TransactionFactory实例
     */
    @Bean(initMethod = "initStrategy") // 初始化完成后调用initStrategy方法
    public TransactionFactory transactionFactory() {
        // 传入策略列表,创建工厂实例
        return new TransactionFactory(transactionList);
    }
}

这种方式更加灵活,适用于通过手动创建Bean的场景,执行顺序在afterPropertiesSet()之后。

4.5 对比与选择

对比维度 构造方法注入 @PostConstruct注解 InitializingBean接口 @Bean注解
所属规范 Java基础特性 JSR-250 Spring框架原生接口 Spring框架注解
依赖耦合度 极低
方法定义灵活性 无独立初始化方法(逻辑写在构造方法) 方法名自定义,支持多个方法(不推荐) 固定方法名afterPropertiesSet() 方法名自定义(需与initMethod参数一致)
异常处理能力 可抛出任意异常(编译时异常需声明) 可抛出任意异常 仅能抛出Exception及其子类 可抛出任意异常
执行顺序 1(最先执行) 3 4 5(最后执行)
适用Bean Spring管理的Bean Spring管理的Bean Spring管理的Bean 手动注册的Bean(第三方类、动态参数Bean)
代码简洁度 最高(无额外注解/接口) 高(仅需一个注解) 中(需实现接口并重写方法) 中(需单独配置类 + Bean方法)

选择建议:

  • 日常开发中优先使用@PostConstruct注解,因其代码更简洁、耦合度更低,符合面向接口编程和低耦合的设计思想;
  • 如果需要与Spring的生命周期深度绑定,或需要处理特定的Spring异常场景,可选择InitializingBean接口;

注意:在同一个Bean的生命周期中,初始化方法的执行顺序为:​
构造方法 → 依赖注入(若有) → @PostConstruct注解方法 → InitializingBean接口的afterPropertiesSet()方法 → @Bean的initMethod指定方法

五、解决策略类膨胀问题

如果交易类型持续增加,策略类数量会膨胀,可通过以下方式优化:

5.1 使用注解标记策略

自定义注解标记策略类,结合工厂模式自动扫描,简化策略注册:

/**
 * 交易策略注解
 * 用于标记策略类对应的交易类型
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TransactionAnn {
    String value(); // 交易类型编码
}

在策略类上添加注解:

@TransactionAnn("1")
@Component
public class DepositApplyServiceImpl extends AbstractTransaction {
    // ...
}

工厂类中通过注解获取编码:

// 替换初始化方法中的逻辑
TransactionAnn ann = transaction.getClass().getAnnotation(TransactionAnn.class);
if (ann != null) {
    TRANSACTION_MAP.put(ann.value(), transaction);
}

5.2 结合模板方法模式

模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中实现。结合策略模式使用时,可将不同交易类型的通用业务流程抽取为模板方法,仅将差异化步骤留给子类实现,进一步减少代码冗余,同时规范业务流程。

5.2.1 改造抽象策略类

我们以交易申请流程为例,一个完整的交易申请通常包含参数校验→订单生成→业务处理→结果返回四个步骤,其中前两步和最后一步是通用的,第三步是差异化的。我们将这些步骤封装为模板方法:

/**
 * 交易策略公共抽象类(结合模板方法模式:定义算法骨架,延迟差异化步骤到子类)
 */
@Slf4j
public abstract class AbstractTransaction implements TransactionService {

    /**
     * 模板方法:交易申请的核心流程(算法骨架)
     * 定义固定的流程步骤,差异化步骤由子类实现
     */
    @Override
    public final R<?> apply(TransactionApply transactionApply) {
        try {
            // 步骤1:通用参数校验(模板方法固定步骤)
            validateParams(transactionApply);
            // 步骤2:通用订单生成(模板方法固定步骤)
            String orderNo = generateOrderNo(transactionApply);
            // 步骤3:差异化业务处理(延迟到子类实现)
            R<?> businessResult = doBusiness(transactionApply, orderNo);
            // 步骤4:通用结果封装(模板方法固定步骤)
            return wrapResult(businessResult, orderNo);
        } catch (Exception e) {
            log.error("交易申请失败", e);
            return R.fail("交易申请失败:" + e.getMessage());
        }
    }

    /**
     * 通用步骤1:参数校验(可由子类重写,覆盖默认校验逻辑)
     */
    protected void validateParams(TransactionApply transactionApply) {
        log.info("执行通用参数校验:{}", transactionApply.getTransactionMode());
        // 默认参数校验逻辑:如交易类型非空、参数对象非空等
        if (transactionApply.getTransactionMode() == null) {
            throw new IllegalArgumentException("交易类型不能为空");
        }
        // 根据交易类型校验对应的参数对象
        TransactionModeEnum mode = TransactionModeEnum.getByCode(transactionApply.getTransactionMode());
        switch (mode) {
            case DEPOSIT:
                if (transactionApply.getDepositApply() == null) {
                    throw new IllegalArgumentException("充值参数不能为空");
                }
                break;
            case WITHDRAW:
                if (transactionApply.getWithdrawApply() == null) {
                    throw new IllegalArgumentException("提现参数不能为空");
                }
                break;
            default:
                throw new IllegalArgumentException("不支持的交易类型");
        }
    }

    /**
     * 通用步骤2:生成订单号(固定逻辑,子类无需重写)
     */
    private String generateOrderNo(TransactionApply transactionApply) {
        // 生成唯一订单号:时间戳+交易类型+随机数
        String orderNo = System.currentTimeMillis() 
                              + "_" + transactionApply.getTransactionMode()
                              + "_" + (int) (Math.random() * 10000);
        log.info("生成交易订单号:{}", orderNo);
        return orderNo;
    }

    /**
     * 差异化步骤3:具体业务处理(由子类实现)
     */
    protected abstract R<?> doBusiness(TransactionApply transactionApply, String orderNo);

    /**
     * 通用步骤4:结果封装(固定逻辑,子类无需重写)
     */
    private R<?> wrapResult(R<?> businessResult, String orderNo) {
        if (businessResult.isSuccess()) {
            return R.success(businessResult.getMessage(), orderNo);
        } else {
            return R.fail(businessResult.getMessage());
        }
    }

    /**
     * 公共的交易验证逻辑(短信验证码确认支付)
     */
    @Override
    public R<?> verify(TransactionVerify transactionVerify) {
        log.info("开始验证交易订单:{}", transactionVerify.getOrderNo());
        // 通用验证逻辑
        log.info("交易订单:{} 验证成功", transactionVerify.getOrderNo());
        return R.success("验证成功");
    }
}

5.2.2 改造具体策略类

此时,具体策略类只需实现doBusiness方法和getTransactionMode方法,无需关注整个流程的骨架,代码更简洁:

/**
 * 充值策略实现类(仅实现差异化业务逻辑)
 */
@Slf4j
@Component
public class DepositApplyServiceImpl extends AbstractTransaction {

    @Override
    public TransactionModeEnum getTransactionMode() {
        return TransactionModeEnum.DEPOSIT;
    }

    /**
     * 差异化业务处理:充值核心逻辑
     */
    @Override
    protected R<?> doBusiness(TransactionApply transactionApply, String orderNo) {
        log.info("处理充值业务,订单号:{},参数:{}", orderNo, transactionApply.getDepositApply());
        // 充值核心逻辑:如调用支付接口、更新用户余额等
        return R.success("充值业务处理成功");
    }

}
/**
 * 提现策略实现类(仅实现差异化业务逻辑)
 */
@Slf4j
@Component
public class WithdrawApplyServiceImpl extends AbstractTransaction {

    @Override
    public TransactionModeEnum getTransactionMode() {
        return TransactionModeEnum.WITHDRAW;
    }

    /**
     * 差异化业务处理:提现核心逻辑
     */
    @Override
    protected R<?> doBusiness(TransactionApply transactionApply, String orderNo) {
        log.info("处理提现业务,订单号:{},参数:{}", orderNo, transactionApply.getWithdrawApply());
        // 提现核心逻辑:如校验用户余额、调用提现接口等
        return R.success("提现业务处理成功");
    }
}

5.2.3 优势

  1. 规范业务流程:所有交易类型都遵循统一的流程(参数校验→订单生成→业务处理→结果返回),避免子类随意修改流程顺序;
  2. 减少重复代码:通用步骤(如订单生成、结果封装)只需在抽象类中实现一次,子类无需重复编写;
  3. 灵活扩展:子类可通过重写通用方法(如validateParams)实现自定义逻辑,同时保留模板方法的骨架;
  4. 职责更清晰:子类只需关注差异化的业务逻辑,无需关心整体流程。

5.3 使用策略注册表

策略注册表是将交易类型与策略类的映射关系从代码中抽离到配置文件(如YAML/Properties)或数据库中,实现策略的动态配置与热更新,避免每次新增策略都需要修改代码并重启服务。这种方式尤其适用于策略类型频繁变更的场景。

5.3.1 定义策略配置实体与配置文件

首先,在application.yml中配置交易类型与策略类的映射关系:

# 交易策略注册表配置
transaction:
  registry:
    # 键:交易类型编码,值:策略类的全限定名
    1: com.xxx.impl.DepositApplyServiceImpl
    2: com.xxx.impl.WithdrawApplyServiceImpl
    3: com.xxx.impl.TransferApplyServiceImpl
    4: com.xxx.impl.RefundApplyServiceImpl

然后,定义配置实体类,绑定上述配置:

/**
 * 交易策略注册表配置实体
 */
@Data
@Component
@ConfigurationProperties(prefix = "transaction")
public class TransactionProperties {
    /**
     * 策略注册表:key=交易类型编码,value=策略类全限定名
     */
    private Map<String, String> registry;
}

5.3.2 改造工厂类

工厂类不再依赖Spring自动注入策略类,而是通过配置文件中的类名,利用反射动态创建策略对象,并支持策略的热更新(通过监听配置文件变化实现):

/**
 * 交易策略工厂(策略注册表模式:基于配置文件动态加载策略)
 */
@Component
@Slf4j
public class TransactionFactory {

    /**
     * 缓存策略对象:key=交易类型编码,value=对应的策略对象(线程安全)
     */
    private static final Map<String, TransactionService> TRANSACTION_MAP = new ConcurrentHashMap<>();

    /**
     * 注入Spring上下文,用于获取由Spring管理的策略Bean(若策略类被@Component标记)
     */
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 注入策略配置
     */
    @Autowired
    private TransactionProperties transactionProperties;

    /**
     * 初始化:加载配置文件中的策略类,创建对象并缓存
     */
    @PostConstruct
    public void init() {
        reload();
    }

    /**
     * 重新加载策略(支持热更新)
     */
    public void reload() {
        Map<String, String> registry = transactionProperties.getRegistry();
        if (registry == null || registry.isEmpty()) {
            log.warn("交易策略注册表为空");
            return;
        }
        // 清空旧的策略缓存
        TRANSACTION_MAP.clear();
        // 遍历配置,创建策略对象
        for (Map.Entry<String, String> entry : registry.entrySet()) {
            String modeCode = entry.getKey();
            String className = entry.getValue();
            try {
                // 方式1:如果策略类由Spring管理(有@Component注解),从Spring上下文获取
                TransactionService transaction = applicationContext.getBean(Class.forName(className));
                // 方式2:如果策略类不是Spring Bean,通过反射创建实例
                // TransactionService transaction = (TransactionService ) Class.forName(className).newInstance();
                TRANSACTION_MAP.put(modeCode, transaction );
                log.info("加载策略成功:{} -> {}", modeCode, className);
            } catch (Exception e) {
                log.error("加载策略失败:{} -> {}", modeCode, className, e);
                throw new RuntimeException("加载策略失败:" + className, e);
            }
        }
    }

    /**
     * 根据交易类型编码获取策略对象
     */
    public static TransactionService getTransaction(String transactionMode) {
        TransactionService transaction = TRANSACTION_MAP.get(transactionMode);
        if (transaction == null) {
            throw new IllegalArgumentException("不支持的交易类型:" + transactionMode);
        }
        return transaction;
    }

    // 测试用:模拟配置更新后调用reload实现热更新
    public void updateConfig(Map<String, String> newRegistry) {
        transactionProperties.setRegistry(newRegistry);
        reload();
    }
}

5.3.3 支持策略热更新(可选)

如果需要实现配置文件修改后自动刷新策略,可结合Spring@RefreshScope或配置文件监听机制(如使用FileSystemWatcher)。以下是基于@RefreshScope的简单实现:

改造配置实体类,添加@RefreshScope注解:

@Data
@Component
@RefreshScope
@ConfigurationProperties(prefix = "transaction")
public class TransactionProperties {
    private Map<String, String> registry;
}

添加接口,手动触发策略重新加载:

/**
 * 策略管理接口(用于热更新策略)
 */
@RestController
@RequestMapping("/manager")
public class ManagerController {

    @Autowired
    private TransactionFactory transactionFactory;

    /**
     * 重新加载策略(配置文件更新后调用)
     */
    @PostMapping("/reload")
    public R<?> reload() {
        transactionFactory.reload();
        return R.success("策略重新加载成功");
    }
}

5.3.4 优势与注意事项

优势:

  1. 动态配置:策略映射关系通过配置文件 /数据库管理,新增或修改策略时无需修改代码;
  2. 支持热更新:配置变更后,可通过接口或自动监听机制重新加载策略,无需重启服务;
  3. 解耦配置与代码:策略类的全限定名与交易类型的映射不再硬编码,便于管理;
  4. 扩展性强:可将策略配置存储到数据库,通过后台管理系统维护,降低运维成本。

注意事项:

  1. 反射性能:通过反射创建策略对象会有轻微的性能损耗,可通过缓存策略对象避免重复创建;
  2. Spring Bean管理:如果策略类依赖Spring的依赖注入(如@Autowired),需通过ApplicationContext获取Bean,而非直接反射创建;
  3. 配置校验:需在初始化时校验配置文件中的类名是否合法、是否实现了TransactionService接口,避免配置错误导致系统异常;
  4. 线程安全:策略对象的缓存需使用线程安全的集合(如ConcurrentHashMap),确保多线程环境下的安全性。

六、总结

本文通过交易业务场景,展示了策略模式 + 工厂模式的组合使用,主要收获如下:

  • 解决了if-else臃肿问题:将不同交易类型的逻辑封装为独立的策略类,代码结构更清晰;
  • 符合开闭原则:新增交易类型时,只需新增策略类并注册到工厂,无需修改原有代码;
  • 提高代码复用性:通过抽象类抽取公共逻辑,避免重复代码;
  • 降低耦合度:客户端通过工厂获取策略,无需了解具体策略实现;
  • 增强可维护性:每个策略类职责单一,便于定位和修改问题。

在实际开发中,策略模式不仅适用于交易场景,还可用于支付方式选择、消息推送方式选择、规则引擎等场景。结合工厂模式后,能更好地解决策略的管理问题,是优化业务逻辑分支的首选方案之一。

posted @ 2022-04-25 16:47  夏尔_717  阅读(1881)  评论(0)    收藏  举报