Spring Boot应用重构咨询:新增Journey无需修改代码方案
重构Spring Boot应用实现Journey的动态扩展
1. 结构化配置文件,实现Journey配置的动态加载
把原来分散的单个Journey配置改成列表形式,借助Spring的@ConfigurationProperties实现配置与代码的解耦,新增Journey仅需添加配置项,无需修改代码。
定义配置绑定类
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; import java.util.List; @Data @Validated @ConfigurationProperties(prefix = "journeys") public class JourneyProperties { private List<JourneyConfig> items; @Data public static class JourneyConfig { @NotNull private Integer id; // 每个Journey的唯一标识 @NotBlank private String name; // 按需添加Journey所需的常量配置,比如计算因子、阈值等 private String calculationFactor; private Integer threshold; } }
配置文件示例(application.properties)
# 已有Journey配置 journeys.items[0].id=1 journeys.items[0].name=Journey1 journeys.items[0].calculationFactor=0.8 journeys.items[0].threshold=100 journeys.items[1].id=2 journeys.items[1].name=Journey2 journeys.items[1].calculationFactor=0.9 journeys.items[1].threshold=150 # 新增Journey仅需追加以下配置,无需改动代码 journeys.items[2].id=3 journeys.items[2].name=Journey3 journeys.items[2].calculationFactor=0.75 journeys.items[2].threshold=120
最后在启动类或配置类上添加@EnableConfigurationProperties(JourneyProperties.class),启用配置绑定功能。
2. 重构SQL查询,支持动态获取所有Journey数据
摒弃硬编码的单Journey查询,改成通用查询逻辑,避免新增Journey时修改SQL:
场景A:每个Journey对应数据库单条记录
直接查询所有Journey的原始数据,无需区分个体:
SELECT journey_id, metric1, metric2, metric3 FROM journey_metrics
场景B:多Journey字段存储在同一行(列存形式)
如果数据是按列存储不同Journey的指标(如journey1_metric1、journey2_metric1),建议用SQL列转行功能(如MySQL的UNION ALL)统一数据格式;若无法修改表结构,可使用MyBatis动态SQL根据配置生成查询字段:
<!-- MyBatis Mapper示例 --> <select id="getAllJourneyMetrics" resultType="com.example.dto.JourneyRawData"> SELECT <foreach collection="journeyIds" item="id" separator=","> journey${id}_metric1 as metric1_${id}, journey${id}_metric2 as metric2_${id} </foreach> FROM journey_data </select>
代码中传入从配置中获取的所有journeyId,即可动态生成适配所有Journey的SQL。
3. 实现通用的Journey映射与计算逻辑
删除getJourney1()、getJourney2()等硬编码方法,编写通用方法根据配置和原始数据生成Journey对象:
定义原始数据DTO
@Data public class JourneyRawData { private Integer journeyId; private Double metric1; private Integer metric2; // 对应SQL查询的字段,按需扩展 }
通用服务逻辑
@Service public class JourneyService { private final JourneyProperties journeyProperties; private final JourneyMapper journeyMapper; // 构造注入依赖 public JourneyService(JourneyProperties journeyProperties, JourneyMapper journeyMapper) { this.journeyProperties = journeyProperties; this.journeyMapper = journeyMapper; } public List<Journey> getAllJourneys() { // 1. 获取所有Journey的原始数据 List<JourneyRawData> rawDataList = journeyMapper.getAllJourneyMetrics(); // 2. 遍历配置,匹配数据并生成Journey对象 return journeyProperties.getItems().stream() .map(config -> buildJourney(config, rawDataList)) .collect(Collectors.toList()); } private Journey buildJourney(JourneyConfig config, List<JourneyRawData> rawDataList) { // 根据配置ID匹配对应原始数据 JourneyRawData rawData = rawDataList.stream() .filter(data -> data.getJourneyId().equals(config.getId())) .findFirst() .orElseThrow(() -> new RuntimeException("Journey " + config.getId() + " 数据不存在")); // 执行原有的计算与映射逻辑,复用配置中的常量 Journey journey = new Journey(); journey.setId(config.getId()); journey.setName(config.getName()); journey.setCalculatedValue(rawData.getMetric1() * Double.parseDouble(config.getCalculationFactor())); journey.setThreshold(config.getThreshold()); // 其他属性映射逻辑 return journey; } }
4. 最终API接口实现
接口层直接调用通用方法,无需修改即可支持新增Journey:
@RestController @RequestMapping("/journeys") public class JourneyController { private final JourneyService journeyService; public JourneyController(JourneyService journeyService) { this.journeyService = journeyService; } @GetMapping public List<Journey> getAllJourneys() { return journeyService.getAllJourneys(); } }
扩展说明
- 配置验证:通过
@Validated和JSR-380注解确保配置完整性,避免无效配置导致的异常。 - 异常处理:可扩展
buildJourney方法的异常逻辑,比如返回空Journey实例或记录告警日志,而非直接抛出异常。 - 性能优化:数据量较大时,可添加缓存机制或优化SQL查询效率。
内容的提问来源于stack exchange,提问作者Keerthana Hemachandran
相关产品推荐
相关产品推荐

