• new BigDecimal(double) (已在你现有规则里)
• new BigDecimal(float) (float 会自动提升为 double,也会命中)
• BigDecimal.valueOf(double)
• BigDecimal.valueOf(float)
在 SonarQube 里最简单、最稳妥的做法就是直接启用 PMD 的 BigDecimalInstantiation 规则,它一口气覆盖了你要拦的 4 种写法:
• new BigDecimal(double) (已在你现有规则里)
• new BigDecimal(float) (float 会自动提升为 double,也会命中)
• BigDecimal.valueOf(double)
• BigDecimal.valueOf(float)
下面给出两条可选路径,任选其一即可。
────────────────────────────────────────────
────────────────────────────────────────────
确认插件
• Administration ➜ Marketplace ➜ PMD
版本 ≥ 3.4.0(最好用 3.9.x;内部自带 PMD 6.5x)。
• 若刚装完,要重启 SonarQube。
激活规则
Rules ➜ Repository 选 “PMD Java”
搜索关键字 BigDecimalInstantiation
打开后勾选 Activate 到你的 Java Quality Profile。
(可选)调参数
点击铅笔图标修改:
• violateConstructor = true (默认)
• violateStaticMethod = true (默认)
保持默认即可同时拦 constructor + valueOf。
重新跑一次扫描
现在 4 种写法都会被报告为 Major (Code Smell)。
如果不想重复报 “new BigDecimal(double)”(SonarJava 的 S2111 也会报),可以把 S2111 在同一个 Profile 里禁用。
────────────────────────────────────────────
────────────────────────────────────────────
• SonarJava 自带 S2111 —— BigDecimal(double) / BigDecimal(float)
已经覆盖 构造函数 的 float + double,
不能 检查 valueOf。
• 如果你坚持连 valueOf 也要禁止,就必须:
────────────────────────────────────────────
────────────────────────────────────────────
• 想“一步搞定” → 安装/启用 PMD 插件里的 BigDecimalInstantiation;啥都不用写。
• 只想拦 constructor → 保留 SonarJava S2111 即可。
• 你现在看到的 “BigDecimal(double) should not be used” 大概率就是 S2111;加 PMD 规则后,再跑分析就能把另外 3 种写法一起抓出来。
下面给出一条「从零开始」编写、打包并在 SonarQube 中启用 自定义 SonarJava 规则的完整示例,专门拦截
BigDecimal.valueOf(double|float)
。
────────────────────────────
────────────────────────────
环境
• JDK 8 或以上 (与 SonarQube Server 兼容即可)
• Maven 3.6+ (推荐)
• 一台可以重启的 SonarQube 服务器(8.x-10.x 均可)
确认服务器已安装 SonarJava 分析器(官方默认自带)。
────────────────────────────
────────────────────────────
mvn archetype:generate -DgroupId=com.example.sonar \
-DartifactId=bigdecimal-rule-plugin \
-DarchetypeGroupId=org.codehaus.mojo.archetypes \
-DarchetypeArtifactId=sonar-plugin-archetype \
-DarchetypeVersion=8.0
cd bigdecimal-rule-plugin
(如果找不到 archetype,可手动建一个普通 Maven 项目,后续内容一样。)
在 pom.xml
核心依赖保持:
<dependencies>
<dependency>
<groupId>org.sonarsource.sonarqubegroupId>
<artifactId>sonar-plugin-apiartifactId>
<version>${sonar.version}version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.sonarsource.javagroupId>
<artifactId>sonar-java-pluginartifactId>
<version>7.23.0.39526version>
<classifier>allclassifier>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.sonarsource.javagroupId>
<artifactId>java-checks-testkitartifactId>
<version>7.23.0.39526version>
<scope>testscope>
dependency>
dependencies>
sonar.version
设成与你服务器一致的版本号(如 10.3.0.82913)。
────────────────────────────
────────────────────────────
src/main/java/com/example/sonar/checks/AvoidBigDecimalValueOfDoubleCheck.java
package com.example.sonar.checks;
import java.util.List;
import org.sonar.check.Rule;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.symbols.Symbol;
import org.sonar.plugins.java.api.semantic.Type;
@Rule(key = "AvoidBigDecimalValueOfDouble")
public class AvoidBigDecimalValueOfDoubleCheck extends IssuableSubscriptionVisitor {
@Override
public List<Tree.Kind> nodesToVisit() {
return List.of(Tree.Kind.METHOD_INVOCATION);
}
@Override
public void visitNode(Tree tree) {
MethodInvocationTree mit = (MethodInvocationTree) tree;
// 1. 方法名必须叫 valueOf
if (!"valueOf".equals(mit.symbol().name())) {
return;
}
// 2. 所属类必须是 java.math.BigDecimal
Symbol owner = mit.symbol().owner();
if (owner == null || !"java.math.BigDecimal".equals(owner.type().fullyQualifiedName())) {
return;
}
// 3. 至少有 1 个实参,而且所有实参都属于 float/double 基本型或装箱型
if (mit.arguments().isEmpty()) {
return;
}
boolean match = mit.arguments().stream().allMatch(arg -> {
Type t = arg.symbolType();
return t.isPrimitive(Type.Primitives.FLOAT) || t.isPrimitive(Type.Primitives.DOUBLE)
|| t.is("java.lang.Float") || t.is("java.lang.Double");
});
if (match) {
reportIssue(tree, "Avoid BigDecimal.valueOf(float/double); use valueOf(long) or string ctor to prevent precision loss.");
}
}
}
────────────────────────────
────────────────────────────
src/main/java/com/example/sonar/rules/CustomJavaRulesDefinition.java
package com.example.sonar.rules;
import com.example.sonar.checks.AvoidBigDecimalValueOfDoubleCheck;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.server.rule.RulesDefinitionXmlLoader;
public class CustomJavaRulesDefinition implements RulesDefinition {
private static final String REPO_KEY = "custom-java";
@Override
public void define(Context context) {
NewRepository repo = context.createRepository(REPO_KEY, "java")
.setName("Custom Java Rules");
// 通过注解扫描
RulesDefinitionXmlLoader loader = new RulesDefinitionXmlLoader();
loader.load(repo, AvoidBigDecimalValueOfDoubleCheck.class.getResourceAsStream("/"), "UTF-8");
repo.done();
}
}
简化做法:也可以不用 XML,直接通过 Scanners 引擎扫描带 @Rule
的类;框架会自动把 rule load 进仓库。若使用自动扫描,就连上面 RulesDefinitionXmlLoader
都省掉,只需:
context.createRepository(REPO_KEY, "java")
.setName("Custom Java Rules")
.addRule(AvoidBigDecimalValueOfDoubleCheck.class)
.done();
src/main/java/com/example/sonar/CustomRulesPlugin.java
package com.example.sonar;
import com.example.sonar.rules.CustomJavaRulesDefinition;
import org.sonar.api.Plugin;
public class CustomRulesPlugin implements Plugin {
@Override
public void define(Context context) {
context.addExtension(CustomJavaRulesDefinition.class);
context.addExtension(com.example.sonar.checks.AvoidBigDecimalValueOfDoubleCheck.class);
}
}
pom.xml
中指定:<packaging>sonar-pluginpackaging>
<properties>
<sonar.plugin.key>custom-bigdecimal-rulessonar.plugin.key>
<sonar.plugin.class>com.example.sonar.CustomRulesPluginsonar.plugin.class>
<sonar.plugin.display.name>Custom BigDecimal Rulessonar.plugin.display.name>
properties>
────────────────────────────
────────────────────────────
src/test/java/com/example/sonar/checks/AvoidBigDecimalValueOfDoubleCheckTest.java
package com.example.sonar.checks;
import org.junit.Test;
import org.sonar.java.checks.verifier.JavaCheckVerifier;
public class AvoidBigDecimalValueOfDoubleCheckTest {
@Test
public void detect_all_variants() {
JavaCheckVerifier.verify("src/test/files/BigDecimalValueOfSample.java",
new AvoidBigDecimalValueOfDoubleCheck());
}
}
BigDecimalValueOfSample.java
中用 // Noncompliant
标注违规行,让 JavaCheckVerifier
自动断言。
────────────────────────────
────────────────────────────
mvn clean package
生成:target/bigdecimal-rule-plugin-1.0-SNAPSHOT.jar
部署到 SonarQube:
cp target/bigdecimal-rule-plugin-*.jar $SONAR_HOME/extensions/plugins/
# 删除同目录下如有旧版 JAR
systemctl restart sonarqube # 或 sonar.sh restart
────────────────────────────
────────────────────────────
AvoidBigDecimalValueOfDouble
➜ Activate 加入你的 Java Quality Profile。BigDecimal.valueOf(double|float)
即会被报告。────────────────────────────
────────────────────────────
• 如还想拦 new BigDecimal(double|float)
,可以把 SonarJava 自带规则 S2111 也加到同一 Profile;
或把本规则扩展为同时监听 NEW_CLASS
,判断构造函数参数类型。
• 若需配置阈值,可在 @Rule
上补充 cardinality=RuleCardinality.MULTIPLE
, 再在 @RuleProperty
中定义参数。
• 针对大量自定义规则,可把每条规则写成独立类 ➜ 统一在 CustomJavaRulesDefinition
中注册。
到此,你就完成了一条自定义 SonarJava 规则的整个闭环:编码→测试→打包→部署→启用→生效。