You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 23:45:45