编程 Modernizer 3.4.0 拿 javaVersion 当尺子:JDK 8 升 17 时 400+ 条遗留 API 规则怎么接

2026-11-05 00:04:10

Modernizer 拿 javaVersion 当尺子:JDK 8 升 17 时怎么接才不白跑

仓库:gaul/modernizer-maven-plugin(Apache-2.0,v3.4.0,Maven Central 坐标 org.gaul:modernizer-maven-plugin)。它的定位是:Detect uses of legacy Java APIs which modern Java versions supersede。

环境要求:Maven 3.6.3+;运行插件用 JDK 8+;从源码构建插件用 JDK 17+。

判定方式:javaVersion 是尺子,不是开关

插件用 ASM 读字节码做内省,不解析源码。默认规则集覆盖 400+ 个遗留 API,包括 Apache Commons、Guava、Joda-Time 等第三方库。

每条规则带一个 version,判定条件是 **`rule.version org.gaul
modernizer-maven-plugin

3.4.0

${maven.compiler.release}

modernizer

verify

modernizer


`mvn modernizer:modernizer` 是单次执行;只有绑到 `verify`,正常构建流程里才会跑。

## javaVersion 绑定 release 属性

不要硬编码 `17`,而是绑 `${maven.compiler.release}`(或 `${maven.compiler.target}`),让检查目标和编译目标同源。

多 module 与父 pom 场景:在父 pom 里把 `17` 定义成属性,插件放进 `
`,子模块继承。注意 `${maven.compiler.release}` 是普通属性占位符,如果你只在 `maven-compiler-plugin` 的 `` 里写了版本而没有定义同名属性,占位符不会被自动填上;要么显式定义属性,要么在子模块里单独覆盖 ``。混合 JDK 的仓库(部分模块留在 8)就在子模块里覆盖该属性,否则复用父 pom 的值会把子模块按 17 去量。

## 三个默认值

- `failOnViolations` 默认 `true`:列完违规按总数让 phase 失败。命令行可用 `-Dmodernizer.failOnViolations` 覆盖。
- `includeTestClasses` 默认 `true`:测试类也会被扫。老项目测试里 Guava 用得往往比主代码还多,第一轮跑出来的量要有心理准备。
- `violationsFile` 是**替换**而不是补充:一旦指定,标准 400+ 条检查全部禁用。要自定义又保留默认规则,用 `violationsFiles` 多文件叠加,后面的覆盖前面的(覆盖范围含 `violationsFile` 和默认规则)。路径可加 `classpath:/` 前缀。

`-Dmodernizer.skip` 可整体跳过。

## 排除项的四种写法,格式不一样

```xml

java/lang/String.getBytes:(Ljava/lang/String;)[B

  • exclusionsFile / exclusions:按 javap 格式关闭单条规则,类型写 java/util/Vector,方法带签名。空行和 # 开头的行忽略。
  • exclusionPatterns:正则,匹配 javap 形式,例如 java/lang/.*。
  • ignorePackages:包前缀,写 foo.bar 会连带忽略 foo.bar.*、foo.bar.baz.*。
  • ignoreClassNamePatterns:全限定类名正则,包路径用 / 分隔(ASM 格式)。

容易混的点:exclusions 走 javap 的点号方法名写法,而 ignoreClassNamePatterns 走斜杠格式;前者精确、后者是正则。写错格式不会报错,只是匹配不上,看起来像"规则没生效"。

另外 ignoreGeneratedClasses 会跳过注解简单名恰为 "Generated"、retention 为 RUNTIME 或 CLASS 的类/方法/构造器,典型是 Lombok 在 lombok.addLombokGeneratedAnnotation=true 时生成的成员。javax.annotation.Generated 和 javax.annotation.processing.Generated 的 retention 都是 SOURCE,编译器阶段已丢弃,识别不到。

@SuppressModernizer 的边界

引入 org.gaul:modernizer-maven-annotations:3.4.0,然后把 @SuppressModernizer 标在类、构造器、方法或类型使用处。插件按简单名匹配,任何包下同名注解都生效,官方依赖只是规范副本。

字段和包不能抑制——@Target 不含它们。这类位置只能落到 exclusions 或 ignoreClassNamePatterns。

换过去不等价的地方

规则抽样里有几条注释值得单独记住,机械替换会改行为:

  • Maps.immutableEntry → Map.entry(Object, Object),version 9:后者拒绝 null key 和 value。
  • Ints.checkedCast(long) → Math.toIntExact(long),version 8:超出范围时抛 ArithmeticException,不是原来的 IllegalArgumentException。
  • BaseEncoding.base16() → HexFormat.of().withUpperCase(),version 17:HexFormat.parseHex 接受小写数字,而原来的 decode 不接受。
  • Lists.reverse(List) → List.reversed(),version 21:目标 21 才可用。
  • IntMath.checkedPow(int, int) → Math.powExact(int, int),version 25:目标 17/21 时这条不会报。

同批里相对直接的:ByteStreams.toByteArray → InputStream.readAllBytes()、ByteStreams.copy → InputStream.transferTo(OutputStream)、Charsets.UTF_8 → StandardCharsets.UTF_8、Lists.newArrayList() → ArrayList<>()、Maps.newHashMapWithExpectedSize → HashMap.newHashMap(int)(version 19)、UnsignedInts.compare → Integer.compareUnsigned、com.google.common.base.Function → java.util.function.Function。另外 Collections.sort 建议改成 List.sort,String.getBytes(String) 改成 String.getBytes(Charset)。

输出接 GitLab 代码质量

默认 CONSOLE,一行一条,带源码位置和建议:

[ERROR] src/main/java/org/example/Foo.java:42: Prefer java.util.ArrayList<>()
[ERROR] src/main/java/org/example/Foo.java:57: Prefer java.nio.charset.StandardCharsets.UTF_8

violationLogLevel 取 ERROR/WARN/INFO/DEBUG,默认 ERROR;failOnViolations 为 true 时列完按总数失败。

接 GitLab 代码质量报告用:


CODE_CLIMATE
MINOR

outputFormat=CODE_CLIMATE 输出符合 Code Climate 规范的 JSON;outputFile 默认 ${project.build.directory}/code-quality.json;codeClimateSeverity 取 INFO/MINOR/MAJOR/CRITICAL/BLOCKER,默认 MINOR。

与相邻工具的分工

同一类问题还有几套工具:Google Error Prone 的 JdkObsolete、Policeman's Forbidden API Checker、Checkstyle 的 IllegalInstantiation/Regexp(可模拟部分功能)、javac -Xlint:deprecated(只看 @Deprecated 标注)、Overstock library-detectors(看 @Beta)。Gradle 侧有 gradle-modernizer-plugin。

选一套当主判据即可。Modernizer 和 forbidden-apis 都会让构建失败、都靠签名匹配,同时装两套容易出现同一条 API 两处告警、两份排除清单要各自维护,规则版本语义还不同——先统一在一处,比并行跑省事。

复制全文 生成海报 modernizer Java Maven 静态检查 JDK升级 ASM

推荐文章

程序员茄子在线接单