Spring中创建Measure对象前如何校验约束并支持手动实例化?
Spring中手动创建对象的可行性与单位转换器校验方案
Spring完全允许手动创建对象,IoC容器只是负责管理核心业务组件(比如转换器、校验器这类需要复用、依赖复杂的对象),像Measure这种承载数据的临时实例,本来就该手动new,不需要都交给Spring管理。
一、重构校验逻辑,剥离Measure类的Spring依赖
把校验逻辑从Measure中抽出来,交给专门的Spring组件处理,让Measure回归纯数据载体的角色:
1. 改造MeasureConstraint抽象类,增加类型匹配方法
给抽象类加一个supports()方法,用来判断当前约束是否适用于目标Unit:
public abstract class MeasureConstraint { // 判断当前约束是否支持传入的Unit类型 public abstract boolean supports(Unit unit); // 核心校验逻辑 public abstract void check(BigDecimal value, Unit unit); }
2. 实现具体约束类(以TemperatureConstraint为例)
@Component // 交给Spring管理,让容器自动发现并收集 public class TemperatureConstraint extends MeasureConstraint { @Override public boolean supports(Unit unit) { // 匹配开尔文单位(假设Kelvin是Unit的实现类) return unit instanceof Kelvin; } @Override public void check(BigDecimal value, Unit unit) { if (value.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException("开尔文温度不能小于0"); } } }
3. 创建MeasureValidator组件,统一处理校验
这个组件由Spring管理,自动注入所有MeasureConstraint实现类,负责根据Unit匹配约束并执行校验:
@Component public class MeasureValidator { private final List<MeasureConstraint> constraints; // 构造方法自动注入所有MeasureConstraint的实现类(Spring会自动收集) public MeasureValidator(List<MeasureConstraint> constraints) { this.constraints = constraints; } public void validate(Measure measure) { // 筛选出当前Unit对应的约束并执行校验 constraints.stream() .filter(constraint -> constraint.supports(measure.getUnit())) .forEach(constraint -> constraint.check(measure.getValue(), measure.getUnit())); } }
4. 清理Measure类,移除Spring注解
Measure是纯数据类,不需要交给Spring管理,去掉@Component等Spring相关注解:
@Getter @Setter @NoArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) public class Measure { BigDecimal value; Unit unit; public Measure(BigDecimal value, Unit unit) { this.value = value; this.unit = unit; } }
二、手动创建Measure对象的正确姿势
在需要创建Measure的业务类(比如你的UnitConverter)中,注入MeasureValidator,先手动new Measure,再调用校验方法:
@Component public class UnitConverter { private final MeasureValidator measureValidator; // 注入校验器 public UnitConverter(MeasureValidator measureValidator) { this.measureValidator = measureValidator; } public Measure convert(Measure source, Unit targetUnit) { // 先校验源Measure的合法性 measureValidator.validate(source); // 执行你的单位转换逻辑,得到目标值 BigDecimal targetValue = calculateConversion(source, targetUnit); // 创建目标Measure并校验 Measure targetMeasure = new Measure(targetValue, targetUnit); measureValidator.validate(targetMeasure); return targetMeasure; } // 封装一个创建合法Measure的方法,方便复用 public Measure createValidMeasure(BigDecimal value, Unit unit) { Measure measure = new Measure(value, unit); measureValidator.validate(measure); return measure; } // 你的转换计算逻辑 private BigDecimal calculateConversion(Measure source, Unit targetUnit) { // 这里写具体的单位转换代码 return BigDecimal.ZERO; } }
三、关键概念澄清与建议
- IoC不是禁止手动创建对象:IoC的核心是把依赖的创建和管理交给Spring,比如MeasureValidator需要的所有MeasureConstraint,不用你手动new,Spring会自动收集注入。但像Measure这种带状态的临时对象,手动创建才是合理的。
- 实体类不要加@Component:Measure是用来承载数据的,每个实例的value和unit都不一样,做成Spring单例会导致状态混乱,完全没必要交给容器管理。
- 职责分离:数据类(Measure)只负责存数据,校验逻辑交给专门的校验器,转换逻辑交给转换器,这样代码更清晰,也避免了不必要的依赖。
内容的提问来源于stack exchange,提问作者Alexey
相关产品推荐
相关产品推荐

