
策略模式Strategy Pattern是后端开发中消灭满屏if-else的终极武器。在 Spring Boot 项目中结合 Spring 的 IoC控制反转机制策略模式的实现会变得极其优雅。一、 业务场景说明在 HR 系统中计算工资的逻辑极其复杂。如果是正式员工 (Regular)需要计算基本工资 绩效 扣除五险一金。如果是实习生 (Intern)按日薪计算且不需要扣除五险一金但要扣除超额的劳务报税。如果是外包人员 (Contractor)按项目结算。如果不走策略模式你的代码里就会写满if (type Regular) { ... } else if (type Intern) { ... }一旦新增员工类型这段代码就会极度膨胀且难以维护。二、 Spring Boot 中的标准代码实现1. 定义策略接口Strategy首先我们定义一个统一的薪资计算接口。import java.math.BigDecimal; public interface SalaryStrategy { /** * 执行具体的薪资计算 */ BigDecimal calculate(Long employeeId); }2. 编写具体的策略实现类Concrete Strategies为每种类型的员工写一个实现类并一定要加上Service或Component注解将其交给 Spring 容器管理。为了方便后续查找我们可以通过注解的value属性给 Bean 起一个特定的名字。import org.springframework.stereotype.Service; import java.math.BigDecimal; // 正式员工策略Bean 名称命名为 regularSalaryStrategy Service(regularSalaryStrategy) public class RegularSalaryStrategy implements SalaryStrategy { Override public BigDecimal calculate(Long employeeId) { System.out.println(查询绩效、扣除五险一金...); return new BigDecimal(15000.00); } } // 实习生策略Bean 名称命名为 internSalaryStrategy Service(internSalaryStrategy) public class InternSalaryStrategy implements SalaryStrategy { Override public BigDecimal calculate(Long employeeId) { System.out.println(按出勤天数结算扣除劳务税...); return new BigDecimal(4000.00); } }3. 构建策略工厂Context / Factory✨ 核心部分在这里我们利用 Spring 强大的集合注入能力把所有的策略统一管理起来。Javaimport org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Map; Component public class SalaryStrategyFactory { /** * 重点来了Spring 会自动查找到所有实现了 SalaryStrategy 接口的 Bean。 * 并把它们注入到这个 Map 中。 * Map 的 Key 就是 Bean 的名字如 regularSalaryStrategyValue 就是 Bean 的实例对象。 */ Autowired private MapString, SalaryStrategy strategyMap; /** * 外部服务调用此方法获取对应的策略 */ public SalaryStrategy getStrategy(String employeeType) { // 拼接出约定的 Bean 名称 String beanName employeeType SalaryStrategy; SalaryStrategy strategy strategyMap.get(beanName); if (strategy null) { throw new IllegalArgumentException(未找到对应的薪资策略: employeeType); } return strategy; } }4. 在业务服务中使用现在主业务流的代码变得极其清爽完全看不到任何的if-else。import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; Service public class PayrollService { Autowired private SalaryStrategyFactory strategyFactory; // 前端传入员工ID和员工类型如 regular 或 intern public void paySalary(Long employeeId, String employeeType) { // 1. 通过工厂拿到对应的策略 SalaryStrategy strategy strategyFactory.getStrategy(employeeType); // 2. 执行计算 BigDecimal finalSalary strategy.calculate(employeeId); System.out.println(员工 employeeId 最终发放薪资: finalSalary); } }三、 Bean 到底是在什么时候注入的这又回到了我们之前聊过的Spring Boot 启动流程和Bean 生命周期。Bean 的注入发生在 Spring Boot 启动核心阶段的refreshContext()刷新上下文这一步。具体到细节层面顺序是这样的扫描阶段 (BeanFactoryPostProcessor)Spring 容器启动时会扫描你代码里所有的Service和Component注解。它发现了RegularSalaryStrategy、InternSalaryStrategy和SalaryStrategyFactory。此时它只是把它们的“图纸BeanDefinition”记录下来了还没有真正new出对象。实例化具体的策略类Spring 开始根据图纸挨个new对象。它发现RegularSalaryStrategy和InternSalaryStrategy没有依赖别人于是非常痛快地把它们实例化完毕并放进了一级缓存单例池 singletonObjects里。此时单例池里有了regularSalaryStrategy - 实例对象A,internSalaryStrategy - 实例对象B。处理策略工厂依赖注入发生在这里接着Spring 准备实例化SalaryStrategyFactory。它new出了工厂对象后进入了生命周期的第二大步——属性赋值Populate Properties。Spring 看到工厂类里面写了Autowired private MapString, SalaryStrategy strategyMap;。Spring 的反射机制开始工作没错这里用到了反射。它去单例池里大喊一声“谁实现了SalaryStrategy接口”刚才准备好的实例对象A和实例对象B齐刷刷地站了出来。Spring 就会自动构建一个 Map把它们按BeanName:实例的键值对存进去然后利用反射类似于Field.set()塞进工厂的strategyMap属性中。启动完成准备接客当 SpringBoot 完全启动并监听 8080 端口时上述的 Map 早就已经在内存里组装好并严阵以待了。等到用户发起 HTTP 请求调接口时程序只是在这个组装好的 Map 里执行了一次极其轻量的get(key)操作而已。Autowiredprivate MapString, SalaryStrategy strategyMap;写了这个就可以把策略实现类就可以自动注入了Spring 容器在底层做了以下几件事1. 触发特殊解析机制普通的Autowired是去找名字或者类型匹配的单个 Bean。但当 Spring 的依赖注入器AutowiredAnnotationBeanPostProcessor发现你要注入的类型是一个Map并且 Key 的类型是String时它会立刻触发“集合注入模式”。2. 全局搜索目标接口Spring 不会去容器里找一个叫strategyMap的 Map 对象。相反它会去看Map 的 Value 泛型类型。 它发现你要的是SalaryStrategy于是 Spring 会去单例池Singleton Pool里把所有实现了SalaryStrategy接口的 Bean 全部搜刮出来。3. 自动组装键值对 (Key-Value)Spring 搜集到这些实现类比如RegularSalaryStrategy和InternSalaryStrategy之后会自动把它们塞进你声明的这个 Map 里Key (键)自动使用该 Bean 在 Spring 容器中的名字比如 regularSalaryStrategy。Value (值)自动使用该 Bean 的实例化对象。⚠️ 成功触发的两个硬性前提条件虽然写起来爽但必须满足两个条件否则 Map 注入进来就是空的或者直接报错实现类必须交给了 Spring 管理你的那些策略实现类上面必须加了Service或Component注解。如果只是自己new出来的普通类Spring 是感知不到的。Map 的 Key 必须是 String 类型如果你写成MapInteger, SalaryStrategySpring 就傻眼了因为它不知道该用什么 Integer 来代表你的 Bean自动注入就会失败。既然 Map 可以这么玩那List行不行当然行而且在某些业务场景下比 Map 更好用如果你写成这样Autowired private ListSalaryStrategy strategyList;Spring 会把所有实现了该接口的 Bean 全部塞进这个 List 里。这有什么用呢—— 责任链模式 / 批量校验器假设你要做一个员工入职校验校验身份证、校验学历、校验黑名单。你可以写 3 个校验类实现同一个Validator接口。然后注入一个ListValidator。 在业务代码里只需要一个for循环遍历这个 List就能把所有校验规则挨个执行一遍。未来如果新增规则只需要新建一个类并加上Component原有代码一行都不用改这种将“策略模式/责任链模式与 Spring 集合注入完美结合”的编码思维正是高级后端工程师和普通 CRUD 工程师的分水岭。