在SpringBoot项目中策略模式的使用方式

 更新时间:2026年01月20日 09:21:26   作者:L_!!!  
本文介绍了如何使用策略模式和Spring依赖注入来优化一个更新考试信息的方法,通过策略模式,每个更新逻辑被封装在独立的策略类中,并且可以通过Spring的依赖注入机制自动管理策略的选择和执行顺序,这种方式使得代码更加解耦、可扩展和维护性好

使用策略模式之前的代码

 @Override
    public void updateExam(String id, ExamUpdateDTO examUpdateDTO) {
        logger.info("Service: 修改考试场次, ID: {}, 数据: {}", id, examUpdateDTO);

        Exam existingExam = mongoDBUtils.findById(id, Exam.class);
        if (existingExam == null) {
            throw new EntityNotFoundException("考试场次不存在: " + id);
        }

        Map<String, Object> examUpdateMap = new HashMap<>();
        if (examUpdateDTO.getExamName() != null) {
            examUpdateMap.put("examName", examUpdateDTO.getExamName());
        }
        if (examUpdateDTO.getExamDescription() != null) {
            examUpdateMap.put("examDescription", examUpdateDTO.getExamDescription());
        }
        if (examUpdateDTO.getStartTime() != null) {
            examUpdateMap.put("startTime", examUpdateDTO.getStartTime());
        }
        if (examUpdateDTO.getDuration() != null) {
            examUpdateMap.put("duration", examUpdateDTO.getDuration());
        }
        if (examUpdateDTO.getStatus() != null) {
            examUpdateMap.put("status", examUpdateDTO.getStatus());
        }

        if (examUpdateMap.isEmpty()) {
            logger.warn("Service: 没有可更新的考试场次字段, ID: {}", id);
            return;
        }
        
        examUpdateMap.put("updateTime", System.currentTimeMillis());

        boolean examUpdated = mongoDBUtils.updateOne(Criteria.where("ID").is(id), examUpdateMap, Exam.class);

        if (!examUpdated) {
            logger.warn("Service: 更新考试场次失败 or no changes made, ID: {}", id);
        }
        logger.info("Service: 考试场次基础信息更新已提交, ID: {}", id);

        boolean needsStudentUpdate = examUpdateDTO.getStartTime() != null || examUpdateDTO.getDuration() != null;

        if (needsStudentUpdate) {
            Map<String, Object> studentUpdateMap = new HashMap<>();
            if (examUpdateDTO.getStartTime() != null) {
                studentUpdateMap.put("startTime", examUpdateDTO.getStartTime());
            }
            if (examUpdateDTO.getDuration() != null) {
                studentUpdateMap.put("duration", examUpdateDTO.getDuration());
            }

            if (!studentUpdateMap.isEmpty()) {
                Criteria studentCriteria = Criteria.where("examID").is(id);
                boolean studentsUpdated = mongoDBUtils.updateMany(studentCriteria, studentUpdateMap, CompetitionSignUpLog.class);
                if (studentsUpdated) {
                    logger.info("Service: 成功级联更新考试场次 ID {} 下学员的 startTime/duration.", id);
                } else {
                    logger.warn("Service: 级联更新考试场次 ID {} 下学员的 startTime/duration 操作未被确认或无匹配学员.", id);
                }
            }
        }
    }

创建策略接口即策略实现类

/**
 * 更新考试场次信息的策略接口
 */
public interface UpdateExamStrategy {
    /**
     * 根据DTO将待更新的字段填充到相应的map中。
     *
     * @param existingExam   当前的考试实体 (可用于比较或特定逻辑)
     * @param dto            考试更新DTO
     * @param examUpdateMap  用于收集考试实体自身更新的字段
     * @param studentUpdateMap 用于收集需要级联更新到学员报名记录的字段
     */
    void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap);
} 
@Component
public class ExamDescriptionUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getExamDescription() != null) {
            examUpdateMap.put("examDescription", dto.getExamDescription());
        }
    }
}
@Component
public class ExamDurationUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getDuration() != null) {
            examUpdateMap.put("duration", dto.getDuration());
            studentUpdateMap.put("duration", dto.getDuration()); // Also update for students
        }
    }
} ```

```java
@Component
public class ExamNameUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getExamName() != null) {
            examUpdateMap.put("examName", dto.getExamName());
        }
    }
} 
@Component
public class ExamStartTimeUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getStartTime() != null) {
            examUpdateMap.put("startTime", dto.getStartTime());
            studentUpdateMap.put("startTime", dto.getStartTime()); // Also update for students
        }
    }
} 
@Component
public class ExamStatusUpdateStrategy implements UpdateExamStrategy {
    @Override
    public void applyUpdate(Exam existingExam, ExamUpdateDTO dto, Map<String, Object> examUpdateMap, Map<String, Object> studentUpdateMap) {
        if (dto.getStatus() != null) {
            examUpdateMap.put("status", dto.getStatus());
        }
    }
}

使用策略模式之后的updateExam方法

@Service
public class ExamServiceImpl implements ExamService {

    private static final Logger logger = LogManager.getLogger(ExamServiceImpl.class);

    @Autowired
    private MongoDBUtils mongoDBUtils;

    private final List<UpdateExamStrategy> updateExamStrategies;

    @Autowired
    public ExamServiceImpl(MongoDBUtils mongoDBUtils, List<UpdateExamStrategy> updateExamStrategies) {
        this.mongoDBUtils = mongoDBUtils; // Ensure mongoDBUtils is assigned if it's not already via @Autowired field injection
        this.updateExamStrategies = updateExamStrategies;
    }
    //...省略其他方法的实现
    @Override
    public void updateExam(String id, ExamUpdateDTO examUpdateDTO) {
        logger.info("Service: 修改考试场次, ID: {}, 数据: {}", id, examUpdateDTO);

        Exam existingExam = mongoDBUtils.findById(id, Exam.class);
        if (existingExam == null) {
            throw new EntityNotFoundException("考试场次不存在: " + id);
        }

        Map<String, Object> examUpdateMap = new HashMap<>();
        Map<String, Object> studentUpdateMap = new HashMap<>();

        // 遍历所有策略并应用更新
        for (UpdateExamStrategy strategy : updateExamStrategies) {
            strategy.applyUpdate(existingExam, examUpdateDTO, examUpdateMap, studentUpdateMap);
        }

        if (!examUpdateMap.isEmpty()) {
            examUpdateMap.put("updateTime", System.currentTimeMillis());
            boolean examUpdated = mongoDBUtils.updateOne(Criteria.where("ID").is(id), examUpdateMap, Exam.class);
            if (!examUpdated) {
                logger.warn("Service: 更新考试场次失败 or no changes made, ID: {}", id);
            } else {
                logger.info("Service: 考试场次基础信息更新已提交, ID: {}", id);
            }
        } else {
            logger.info("Service: 没有可更新的考试场次字段, ID: {}", id);
            // 如果 examUpdateMap 为空,但 studentUpdateMap 不为空,则不需要返回
            // 即使考试本身没有变化,关联的学生信息可能仍需更新
        }

        if (!studentUpdateMap.isEmpty()) {
            Criteria studentCriteria = Criteria.where("examID").is(id);
            boolean studentsUpdated = mongoDBUtils.updateMany(studentCriteria, studentUpdateMap, CompetitionSignUpLog.class);
            if (studentsUpdated) {
                logger.info("Service: 成功级联更新考试场次 ID {} 下学员的相关信息.", id);
            } else {
                logger.warn("Service: 级联更新考试场次 ID {} 下学员信息的操作未被确认或无匹配学员.", id);
            }
        }
    }
 }   

在这个场景中,策略的选择是由Spring的依赖注入机制自动完成的。

具体工作原理:

1、策略的收集

  • 由于每个策略类(如ExamDescriptionUpdateStrategyExamStatusUpdateStrategyExamStartTimeUpdateStrategy)都标注了@Component注解
  • Spring会自动扫描并实例化这些实现了UpdateExamStrategy接口的类
  • 所有策略实例会被自动注入到ExamServiceImpl中的updateExamStrategies列表中

2、策略的顺序

Spring在注入时会按照以下规则确定策略在列表中的顺序:

  • 默认按照类名的字母顺序
  • 可以通过@Order注解或实现Ordered接口来自定义顺序

3、策略的使用

@Autowired
public ExamServiceImpl(MongoDBUtils mongoDBUtils, List<UpdateExamStrategy> updateExamStrategies) {
    this.mongoDBUtils = mongoDBUtils;
    this.updateExamStrategies = updateExamStrategies;  // Spring自动注入所有策略
}

4、遍历执行

for (UpdateExamStrategy strategy : updateExamStrategies) {
    strategy.applyUpdate(existingExam, examUpdateDTO, examUpdateMap, studentUpdateMap);
}
  • 每次遍历会取出列表中的一个策略实例
  • 每个策略实例都是一个具体的策略类的对象(如ExamDescriptionUpdateStrategy的实例)
  • 调用策略的applyUpdate方法时,会执行对应策略类中的具体实现

这是一个典型的策略模式结合Spring依赖注入的应用,它的好处是:

  • 解耦:各个更新策略相互独立
  • 可扩展:添加新的更新策略只需创建新的策略类并添加@Component注解
  • 维护性好:每个策略类负责处理特定的更新逻辑

总结

这里添加了一个构造函数来注入 mongoDBUtils 和 updateExamStrategies。如果你的项目中 mongoDBUtils 已经通过字段上的 @Autowired 注入,那么在构造函数中再次赋值是多余的,但通常无害。Spring 的依赖注入会处理好这些。

主要逻辑已更改为: 初始化 examUpdateMap 和 studentUpdateMap。 遍历所有注入的 UpdateExamStrategy 策略。 每个策略根据 ExamUpdateDTO 的内容,决定是否要向 examUpdateMap(用于更新 Exam 实体本身)或 studentUpdateMap(用于级联更新 CompetitionSignUpLog 实体)添加键值对。

如果 examUpdateMap 不为空,则更新 Exam 实体。 如果 studentUpdateMap 不为空,则更新相关的CompetitionSignUpLog 实体。

这种方式将每个字段的更新逻辑(包括是否需要级联更新)封装在各自的策略类中,使得 updateExam 方法本身更加简洁,并且易于扩展新的更新逻辑。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中如何使用Response重定向

    Java中如何使用Response重定向

    这篇文章主要介绍了Java中如何使用Response重定向,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Jmeter BlazeMeter实现web录制过程

    Jmeter BlazeMeter实现web录制过程

    BlazeMeter是一款与Apache JMeter兼容的chrome插件,采用BlazeMeter可以方便的进行流量录制和脚本生成,作为接口测试脚本编写的一个基础,这篇文章主要介绍了Jmeter BlazeMeter实现web录制,需要的朋友可以参考下
    2021-12-12
  • Java中 this和super的用法与区别小结

    Java中 this和super的用法与区别小结

    在Java的学习与开发者我们经常遇到this和super关键字,本文主要介绍了Java中 this和super的用法与区别小结,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • Java多线程程序中synchronized修饰方法的使用实例

    Java多线程程序中synchronized修饰方法的使用实例

    synchronized关键字主要北用来进行线程同步,这里我们主要来演示Java多线程程序中synchronized修饰方法的使用实例,需要的朋友可以参考下:
    2016-06-06
  • 浅谈HBase在SpringBoot项目里的应用(含HBaseUtil工具类)

    浅谈HBase在SpringBoot项目里的应用(含HBaseUtil工具类)

    这篇文章主要介绍了浅谈HBase在SpringBoot项目里的应用(含HBaseUtil工具类),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • java对象类型转换和多态性(实例讲解)

    java对象类型转换和多态性(实例讲解)

    下面小编就为大家带来一篇java对象类型转换和多态性(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Maven之远程仓库的配置详解

    Maven之远程仓库的配置详解

    这篇文章主要介绍了Maven之远程仓库的配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤及问题解决

    IntelliJ IDEA 中配置 Spring MVC 环境的详细步

    这篇文章主要介绍了IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤及问题解决,本文分步骤结合实例给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-04-04
  • Java使用lambda自定义Arrays.sort排序规则说明

    Java使用lambda自定义Arrays.sort排序规则说明

    这篇文章主要介绍了Java使用lambda自定义Arrays.sort排序规则说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • Java实现的文件上传下载工具类完整实例【上传文件自动命名】

    Java实现的文件上传下载工具类完整实例【上传文件自动命名】

    这篇文章主要介绍了Java实现的文件上传下载工具类,结合完整实例形式分析了java针对文件上传下载操作的相关实现技巧,并且针对上传文件提供了自动命名功能以避免文件命名重复,需要的朋友可以参考下
    2017-11-11

最新评论