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

如何使用ArchUnit强制Java中BigDecimal仅通过MathUtils实例化

一、正确编写ArchUnit规则

你当前的规则只覆盖了直接调用BigDecimal构造器的场景,但实际开发中很多人会用BigDecimal.valueOf()这类静态工厂方法创建实例,所以需要补充对静态工厂方法的限制。以下是更完善的规则:

@ArchTest
public static final ArchRule bigDecimalMustBeCreatedViaMathUtils = ArchRuleDefinition.noClasses()
    .that().doNotHaveFullyQualifiedName("a.b.c.MathUtils")
    .should().callConstructor(BigDecimal.class)
    .orShould().callMethod(BigDecimal.class, "valueOf", Number.class)
    .orShould().callMethod(BigDecimal.class, "valueOf", long.class)
    .orShould().callMethod(BigDecimal.class, "valueOf", double.class)
    .orShould().callMethod(BigDecimal.class, "of", int.class)
    .orShould().callMethod(BigDecimal.class, "of", long.class)
    .orShould().callMethod(BigDecimal.class, "of", BigInteger.class)
    .because("所有BigDecimal实例必须通过MathUtils创建,保证MathContext配置一致");

如果想覆盖所有BigDecimal的实例创建静态方法,同时排除常量引用(比如BigDecimal.ZERO),可以用更宽泛的匹配:

@ArchTest
public static final ArchRule bigDecimalCreationRestricted = ArchRuleDefinition.noClasses()
    .that().doNotHaveFullyQualifiedName("a.b.c.MathUtils")
    .should().callConstructor(BigDecimal.class)
    .orShould().callMethod(BigDecimal.class, "*")
    .that().isStatic()
    .and().doesNotHaveName("ZERO", "ONE", "TEN")
    .because("所有BigDecimal实例必须通过MathUtils创建");

如果MathUtils允许子类扩展,把doNotHaveFullyQualifiedName换成areNotAssignableTo(MathUtils.class)会更灵活。

二、Java中强制执行此类规则的替代方案

除了ArchUnit,还有几种实用的约束方式:

  • Checkstyle/SonarQube自定义规则:
    编写自定义扫描规则,检测代码中直接创建BigDecimal的语句(构造器调用、静态工厂方法调用),在代码扫描阶段抛出警告或错误。SonarQube可通过自定义Java规则插件实现,Checkstyle也支持自定义检查器。

  • 编译时注解处理器:
    编写注解处理器,在编译阶段遍历抽象语法树(AST),一旦发现直接实例化BigDecimal的代码就抛出编译错误。这种方式能在开发阶段及时阻止违规代码,无需等到测试或扫描环节。

  • 代码审查强制:
    在合并请求(PR)模板中明确要求检查BigDecimal的创建方式,由团队成员人工审查。适合小型团队,但依赖执行力度,自动化程度较低。

  • 封装隐藏BigDecimal:
    让MathUtils提供所有业务需要的数值操作方法,业务代码仅使用MathUtils返回的BigDecimal实例,不直接引入BigDecimal类。配合ArchUnit规则禁止其他类导入java.math.BigDecimal,进一步强化约束:

    @ArchTest
    public static final ArchRule noDirectBigDecimalImport = ArchRuleDefinition.noClasses()
        .that().doNotHaveFullyQualifiedName("a.b.c.MathUtils")
        .should().importClass(BigDecimal.class)
        .because("禁止直接导入BigDecimal,必须通过MathUtils使用");
    

内容的提问来源于stack exchange,提问作者Vipin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 22:57:40