Checkstyle

← 返回目录 > ← 返回总览

相关工具推荐

工具 说明
PMD Java 静态代码分析工具,侧重潜在缺陷与不良实践检测
SpotBugs Java 字节码级缺陷检测工具,基于 Bug Patterns 发现问题
Google-Java-Format Google 出品的 Java 代码格式化工具,与 Google Java Style Guide 配合使用
Spotless 多语言代码格式化统一框架,支持 Maven/Gradle 集成

1. 简介

Checkstyle 是一款面向 Java 语言的开源静态代码分析工具,由 Oliver Burn 于 2001 年创建,是 Java 生态中代码风格与规范检查的主流工具之一。它通过解析 Java 源代码的抽象语法树(AST),结合预定义或自定义的规则集,对代码进行逐行、逐方法、逐类级别的合规性扫描与报告生成。

  • 主要检查语言:Java
  • 主要检查能力:检测 Java 代码风格、命名规范、Javadoc 注释、代码复杂度、导入语句、代码块结构等;涵盖代码风格检查、编码规范检查、代码复杂度检查
  • 核心检查原理:基于 ANTLR4 解析 Java 源码生成 AST,采用 Visitor 模式遍历 AST 节点执行检查
  • 检查规则/选项:约 180+ 项标准检查(按 naming、coding、design、imports、javadoc、whitespace 等类别组织),全量规则列表
  • GitHub 仓库https://github.com/checkstyle/checkstyle(8,948 stars)
  • 开源协议:LGPL-2.1-or-later
  • 最新稳定版本:v10.21.4
  • 运行环境要求:13.x 需 JRE 21+,11.x/12.x 需 JRE 17+,10.x 需 JRE 11+,7.x-9.x 需 JRE 8+;支持解析 Java 22 及以下版本的所有语言特性
  • 误报率:低

2. 官方文档

资源 链接
官方网站 https://checkstyle.org/
配置文件说明 https://checkstyle.org/config.html
所有检查规则一览 https://checkstyle.org/checks.html
命令行使用文档 https://checkstyle.org/cmdline.html
过滤器(Filters)文档 https://checkstyle.org/config_filters.html
文件过滤器文档 https://checkstyle.org/config_filefilters.html
属性类型说明 https://checkstyle.org/property_types.html
Ant 任务文档 https://checkstyle.org/anttask.html
样式配置说明(Google/Sun) https://checkstyle.org/style_configs.html
Google Java Style 配置详情 https://checkstyle.org/google_style.html
Sun Code Conventions 配置详情 https://checkstyle.org/sun_style.html
自定义检查开发指南 https://checkstyle.org/writingchecks.html
贡献指南 https://checkstyle.org/contributing.html
GitHub Releases https://github.com/checkstyle/checkstyle/releases
Maven Central 依赖 https://central.sonatype.com/artifact/com.puppycrawl.tools/checkstyle
Google Java Style Guide(参考) https://google.github.io/styleguide/javaguide.html

3. 社区优秀实践

3.1 Spring Framework

Spring Framework 是 Java 生态中最具影响力的开源项目之一,在构建流程中深度集成了 Checkstyle。Spring 团队维护了自己的代码规范配置,基于 Google Java Style 进行了定制扩展,并通过 spring-javaformat 项目提供了配套的格式化工具。

  • 仓库地址https://github.com/spring-projects/spring-framework
  • 规范配置位置src/checkstyle/checkstyle.xml
  • 特点:使用 spring-javaformat-checkstyle 扩展模块,包含 SpringHeaderCheck(版权声明检查)、HideUtilityClassConstructor 等自定义规则
  • Gradle 集成:通过 checkstyle 插件在 checkstyleMain / checkstyleTest 任务中执行
// Spring Framework 的 build.gradle 中的 Checkstyle 配置片段
checkstyle {
    toolVersion = "10.18.1"
    configDir = file("src/checkstyle")
    configProperties = [config_loc: file("src/checkstyle").absolutePath]
}

3.2 Apache Maven(maven-checkstyle-plugin)

Apache Maven 官方提供的 maven-checkstyle-plugin 是 Checkstyle 在 Java 构建生态中应用最广泛的集成方式。该插件内置了 maven_checks.xml 配置文件,专门用于 Maven 插件和组件的代码规范检查,是 Maven 项目接入 Checkstyle 的首选方案。

  • 仓库地址https://github.com/apache/maven-checkstyle-plugin
  • 内置规范maven_checks.xml(Maven 项目专用规范)
  • 特点:与 Maven 生命周期深度绑定,支持在 validateverify 等阶段自动执行检查,可生成 XML/HTML 格式的检查报告
<!-- Maven 项目中使用内置 maven_checks.xml -->
<configuration>
    <configLocation>maven_checks.xml</configLocation>
</configuration>

3.3 Google Guava

Google Guava 是 Google 开源的核心 Java 库,严格遵循 Google Java Style Guide,使用 Checkstyle 进行代码规范强制检查。Google 维护的 google_checks.xml 是 Checkstyle 内置的两大标准配置之一,被广泛作为 Java 项目的规范基线。


4. 工具配置说明

以下推荐两种方案:一是直接使用 Google Java Style 配置(开箱即用),二是基于 Google 规范定制的团队配置(推荐)。

4.1 配置文件说明

Checkstyle 提供三种内置的标准配置文件,均包含在 Checkstyle 的 JAR 包中,同时支持自定义 XML 配置文件:

配置文件 用途 使用场景
google_checks.xml 基于 Google Java Style Guide 的内置配置 通用项目,推荐大多数团队使用
sun_checks.xml 基于 Sun Code Conventions 的内置配置 传统 Java 项目
maven_checks.xml Maven 项目专用规范 Apache Maven 插件/组件开发
checkstyle.xml 自定义团队规范配置 需要定制规则的项目,放置在 config/checkstyle/ 目录下

配置文件源码位置

4.2 checkstyle.xml 配置详解

Checkstyle 配置文件采用 XML 格式,根元素为 <module name="Checker">,通过嵌套 <module> 定义检查规则,通过 <property> 配置规则参数。

配置项说明(Checker 模块属性):

配置项 类型 说明
charset string 配置文件使用的字符编码
severity string 违规严重级别(errorwarninginfo),设为 error 时违规会导致构建失败
fileExtensions string 检查的文件扩展名列表(逗号分隔)

自定义配置文件最小示例

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
        "https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
    <property name="charset" value="UTF-8"/>
    <property name="fileExtensions" value="java, properties, xml"/>
    <property name="severity" value="error"/>

    <!-- 排除 module-info.java -->
    <module name="BeforeExecutionExclusionFileFilter">
        <property name="fileNamePattern" value="module\-info\.java$"/>
    </module>

    <!-- 引入抑制规则文件 -->
    <module name="SuppressionFilter">
        <property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
        <property name="optional" value="true"/>
    </module>

    <!-- 文件级检查 -->
    <module name="FileTabCharacter"/>
    <module name="LineLength">
        <property name="fileExtensions" value="java"/>
        <property name="max" value="120"/>
    </module>

    <!-- AST 级检查 -->
    <module name="TreeWalker">
        <module name="PackageName"/>
        <module name="TypeName"/>
        <module name="MethodName"/>
        <module name="MemberName"/>
        <module name="ParameterName"/>
        <module name="ConstantName"/>
    </module>
</module>

推荐配置方案

方案一:直接使用 Google Java Style

Google Java Style 是 Checkstyle 内置的标准配置,开箱即用,适合大多数 Java 项目。通过 --config 参数或构建工具的 configLocation 指定 google_checks.xml 即可启用。

Google Checks 的主要规则包括:

  • 行长度限制 100 字符
  • 缩进使用 2 个空格
  • 命名规范:类名 UpperCamelCase、方法名 lowerCamelCase、常量 UPPER_SNAKE_CASE、包名全小写
  • 禁止星号导入(import *
  • Javadoc 要求:public 方法必须有 Javadoc
  • 代码块风格:K&R 风格(左大括号不换行)

方案二:基于 Google 规范的团队定制配置(推荐)

以下是一个基于 Google Java Style 优化后的团队配置文件,放宽了部分严格规则,增加了实用性检查。

config/checkstyle/checkstyle.xml

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
        "https://checkstyle.org/dtds/configuration_1_3.dtd">

<!--
    基于 Google Java Style Guide 的团队定制 Checkstyle 配置。
    参考规范:https://google.github.io/styleguide/javaguide.html
    官方文档:https://checkstyle.org
-->
<module name="Checker">

    <!-- 启用 @SuppressWarnings 注解抑制 -->
    <module name="SuppressWarningsFilter"/>

    <!-- 字符编码 -->
    <property name="charset" value="UTF-8"/>
    <!-- 违规严重级别设为 error,使构建在检查出问题时失败 -->
    <property name="severity" value="error"/>
    <!-- 检查的文件类型 -->
    <property name="fileExtensions" value="java, properties, xml"/>

    <!-- 排除 module-info.java 文件 -->
    <module name="BeforeExecutionExclusionFileFilter">
        <property name="fileNamePattern" value="module\-info\.java$"/>
    </module>

    <!-- 引入外部抑制规则文件 -->
    <module name="SuppressionFilter">
        <property name="file" value="${config_loc}/checkstyle-suppressions.xml"
                  default="config/checkstyle/checkstyle-suppressions.xml"/>
        <property name="optional" value="true"/>
    </module>

    <!-- ========== 文件级检查 ========== -->

    <!-- 禁止使用 Tab 字符,强制使用空格缩进 -->
    <module name="FileTabCharacter">
        <property name="eachLine" value="true"/>
    </module>

    <!-- 行长度限制 120 字符(Google 默认 100,团队放宽至 120) -->
    <module name="LineLength">
        <property name="fileExtensions" value="java"/>
        <property name="max" value="120"/>
        <property name="ignorePattern"
                  value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
    </module>

    <!-- 文件末尾必须有换行符 -->
    <module name="NewlineAtEndOfFile"/>

    <!-- 文件长度不超过 2000 行 -->
    <module name="FileLength">
        <property name="max" value="2000"/>
    </module>

    <!-- ========== AST 级检查(TreeWalker) ========== -->
    <module name="TreeWalker">

        <!-- 外部类型名称必须与文件名匹配 -->
        <module name="OuterTypeFilename"/>

        <!-- 禁止非法的字符串标记(禁止八进制转义和 Unicode 转义) -->
        <module name="IllegalTokenText">
            <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
            <property name="format"
                      value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
            <property name="message"
                      value="禁止使用八进制转义或 Unicode 转义字符"/>
        </module>

        <!-- ========== 导入检查 ========== -->

        <!-- 禁止星号导入 -->
        <module name="AvoidStarImport"/>

        <!-- 禁止冗余导入 -->
        <module name="RedundantImport"/>

        <!-- 禁止未使用的导入 -->
        <module name="UnusedImports"/>

        <!-- 禁止从 sun.* 包导入 -->
        <module name="IllegalImport">
            <property name="illegalPkgs" value="sun"/>
        </module>

        <!-- 导入语句排序:静态导入放在普通导入之后 -->
        <module name="CustomImportOrder">
            <property name="sortImportsInGroupAlphabetically" value="true"/>
            <property name="separateLineBetweenGroups" value="true"/>
            <property name="customImportOrderRules"
                      value="STATIC###THIRD_PARTY_PACKAGE"/>
        </module>

        <!-- ========== 命名检查 ========== -->

        <!-- 常量名:全大写,单词间下划线分隔,如 MAX_COUNT -->
        <module name="ConstantName">
            <property name="format" value="^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"/>
        </module>

        <!-- 局部 final 变量名:camelCase,允许以大写字母开头 -->
        <module name="LocalFinalVariableName">
            <property name="format" value="^[a-z][a-zA-Z0-9]*$|^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*$)"/>
        </module>

        <!-- 局部变量名:camelCase -->
        <module name="LocalVariableName">
            <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
        </module>

        <!-- 成员变量名:camelCase -->
        <module name="MemberName">
            <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
        </module>

        <!-- 方法名:camelCase -->
        <module name="MethodName">
            <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
        </module>

        <!-- 包名:全小写 -->
        <module name="PackageName">
            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
        </module>

        <!-- 参数名:camelCase -->
        <module name="ParameterName">
            <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
        </module>

        <!-- 类/接口名:UpperCamelCase -->
        <module name="TypeName">
            <property name="format" value="^[A-Z][a-zA-Z0-9]*$"/>
        </module>

        <!-- ========== 代码块检查 ========== -->

        <!-- 空代码块检查 -->
        <module name="EmptyBlock">
            <property name="option" value="TEXT"/>
            <property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
        </module>

        <!-- 左大括号位置:不换行(K&R 风格) -->
        <module name="LeftCurly">
            <property name="tokens"
                      value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
                             INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
                             LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
                             LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE,
                             METHOD_DEF, OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
        </module>

        <!-- 右大括号位置 -->
        <module name="RightCurly">
            <property name="id" value="RightCurlySame"/>
            <property name="tokens"
                      value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
                             LITERAL_DO"/>
        </module>
        <module name="RightCurly">
            <property name="id" value="RightCurlyAlone"/>
            <property name="option" value="alone"/>
            <property name="tokens"
                      value="CLASS_DEF, CTOR_DEF, ENUM_DEF, INTERFACE_DEF, METHOD_DEF, RECORD_DEF,
                             COMPACT_CTOR_DEF, STATIC_INIT, RECORD_DEF, OBJBLOCK, LAMBDA"/>
        </module>

        <!-- 需要 else 时禁止省略大括号 -->
        <module name="NeedBraces">
            <property name="tokens"
                      value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
        </module>

        <!-- ========== 空白符检查 ========== -->

        <!-- 方法调用后空格检查 -->
        <module name="MethodParamPad"/>

        <!-- 圆括号内无空格 -->
        <module name="ParenPad"/>

        <!-- 运算符周围空格 -->
        <module name="WhitespaceAround">
            <property name="allowEmptyConstructors" value="true"/>
            <property name="allowEmptyLambdas" value="true"/>
            <property name="allowEmptyMethods" value="true"/>
            <property name="allowEmptyTypes" value="true"/>
            <property name="allowEmptyLoops" value="true"/>
            <property name="tokens"
                      value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN,
                             BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL,
                             GE, GT, LAMBDA, LAND, LCURLY, LE, LITERAL_RETURN, LT, MINUS,
                             MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN,
                             QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN, STAR,
                             STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
        </module>

        <!-- 泛型中 > 后无空格,< 前无空格 -->
        <module name="GenericWhitespace"/>

        <!-- 数组定义风格:Java 风格 String[] args,而非 C 风格 String args[] -->
        <module name="ArrayTypeStyle"/>

        <!-- switch 语句中必须有 default -->
        <module name="DefaultComesLast"/>

        <!-- fall-through 检查 -->
        <module name="FallThrough"/>

        <!-- ========== 代码质量检查 ========== -->

        <!-- 禁止使用 System.out 和 System.err,应使用日志框架 -->
        <module name="Regexp">
            <property name="format" value="System\.(out|err)\.print"/>
            <property name="illegalPattern" value="true"/>
            <property name="message" value="请使用 SLF4J 等日志框架替代 System.out/err"/>
        </module>

        <!-- long 类型字面量使用大写 L -->
        <module name="UpperEll"/>

        <!-- 禁止在 if/while 条件中使用赋值 -->
        <module name="InnerAssignment"/>

        <!-- 禁止空语句 -->
        <module name="EmptyStatement"/>

        <!-- ========== Javadoc 检查 ========== -->

        <!-- public 方法必须有 Javadoc -->
        <module name="JavadocMethod">
            <property name="scope" value="public"/>
            <property name="allowMissingParamTags" value="true"/>
            <property name="allowMissingReturnTag" value="true"/>
        </module>

        <!-- public 类必须有 Javadoc -->
        <module name="JavadocType">
            <property name="scope" value="public"/>
        </module>

        <!-- ========== 修饰符检查 ========== -->

        <!-- 修饰符顺序:public protected private abstract static final transient volatile synchronized native strictfp -->
        <module name="ModifierOrder"/>

        <!-- 冗余修饰符检查:确保接口方法不需要 public abstract -->
        <module name="RedundantModifier"/>

    </module>
</module>

配套的抑制规则文件 config/checkstyle/checkstyle-suppressions.xml

<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
        "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
        "https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
    <!-- 排除自动生成的代码 -->
    <suppress files=".*[\\/]generated[\\/].*" checks=".*"/>
    <suppress files=".*[\\/]generated-sources[\\/].*" checks=".*"/>

    <!-- 排除测试代码的 Javadoc 检查 -->
    <suppress files=".*Test\.java" checks="JavadocMethod"/>
    <suppress files=".*Test\.java" checks="JavadocType"/>

    <!-- 排除配置类的行长度检查 -->
    <suppress files=".*Application\.java" checks="LineLength"/>

    <!-- 排除常量类的魔法数字检查 -->
    <suppress files=".*Constant(s)?\.java" checks="MagicNumber"/>
</suppressions>

4.3 规则说明对照表

规则模块 说明 默认值/配置
FileTabCharacter 禁止使用 Tab 字符 每行检查
LineLength 行长度限制 120 字符
NewlineAtEndOfFile 文件末尾换行 必须有换行
FileLength 文件长度限制 2000 行
AvoidStarImport 禁止星号导入 禁止 import xxx.*
RedundantImport 禁止冗余导入 自动检测
UnusedImports 禁止未使用的导入 自动检测
IllegalImport 禁止非法包导入 禁止 sun.*
CustomImportOrder 导入排序 静态导入在普通导入之后
ConstantName 常量命名 ^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$
LocalVariableName 局部变量命名 ^[a-z][a-zA-Z0-9]*$
MemberName 成员变量命名 ^[a-z][a-zA-Z0-9]*$
MethodName 方法命名 ^[a-z][a-zA-Z0-9]*$
TypeName 类/接口命名 ^[A-Z][a-zA-Z0-9]*$
PackageName 包名命名 ^[a-z]+(\.[a-z][a-z0-9]*)*$
LeftCurly 左大括号位置 不换行(K&R 风格)
RightCurly 右大括号位置 独占一行
NeedBraces 必须使用大括号 if/for/while/do-while
WhitespaceAround 运算符周围空格 必须有空格
GenericWhitespace 泛型空白符 <> 前后无空格
EmptyBlock 空代码块 禁止(TEXT 模式)
EmptyStatement 空语句 禁止
FallThrough switch fall-through 禁止无注释的 fall-through
DefaultComesLast switch default 位置 default 必须在最后
UpperEll long 类型字面量 必须使用大写 L
ArrayTypeStyle 数组声明风格 Java 风格 String[]
ModifierOrder 修饰符顺序 按 Java 规范排序
Regexp 正则表达式检查 禁止 System.out/err.print
JavadocMethod 方法 Javadoc public 方法必须有
JavadocType 类 Javadoc public 类必须有

5. 主流集成方式

5.1 Maven 集成(pom.xml)

Maven 是 Java 生态中最主流的构建工具,maven-checkstyle-plugin 是 Checkstyle 最推荐的集成方式。

<properties>
    <checkstyle.version>13.5.0</checkstyle.version>
    <maven-checkstyle-plugin.version>3.6.0</maven-checkstyle-plugin.version>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-checkstyle-plugin</artifactId>
            <version>${maven-checkstyle-plugin.version}</version>
            <dependencies>
                <dependency>
                    <groupId>com.puppycrawl.tools</groupId>
                    <artifactId>checkstyle</artifactId>
                    <version>${checkstyle.version}</version>
                </dependency>
            </dependencies>
            <configuration>
                <!-- 指定配置文件路径 -->
                <configLocation>config/checkstyle/checkstyle.xml</configLocation>
                <!-- 抑制规则文件路径 -->
                <suppressionsLocation>config/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
                <!-- 字符编码 -->
                <encoding>UTF-8</encoding>
                <!-- 控制台输出检查结果 -->
                <consoleOutput>true</consoleOutput>
                <!-- 发现违规时是否使构建失败 -->
                <failsOnError>true</failsOnError>
                <!-- 是否包含测试源码目录 -->
                <includeTestSourceDirectory>true</includeTestSourceDirectory>
            </configuration>
            <executions>
                <execution>
                    <id>checkstyle-validation</id>
                    <phase>validate</phase>
                    <goals>
                        <goal>check</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

执行命令:

# 执行 Checkstyle 检查
mvn checkstyle:check

# 生成 HTML 报告
mvn checkstyle:checkstyle
# 报告位于 target/site/checkstyle.html

Maven Checkstyle Plugin 文档:https://maven.apache.org/plugins/maven-checkstyle-plugin/

增量检查:在 Maven 中通过 includes / excludes 参数限定检查范围:

<configuration>
    <configLocation>config/checkstyle/checkstyle.xml</configLocation>
    <!-- 只检查主源码目录 -->
    <includeTestSourceDirectory>false</includeTestSourceDirectory>
    <!-- 指定包含的源码目录 -->
    <sourceDirectories>
        <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
    </sourceDirectories>
    <!-- 排除生成的代码 -->
    <excludes>**/generated/**,**/target/**</excludes>
</configuration>

5.2 pre-commit 集成(通过 local hook)

Java 项目可通过 repo: local + language: system 将 Checkstyle 集成为 pre-commit local hook。这样 pre-commit run --all-files 一条命令即可在本地提交前和 CI 中执行相同的检查,保证本地与 CI 门禁一致性。

Maven 项目示例:

# .pre-commit-config.yaml
- repo: local
  hooks:
    - id: checkstyle-check
      name: Checkstyle check
      entry: mvn checkstyle:check
      language: system
      files: \.java$
      pass_filenames: false

Gradle 项目将 entry 替换为 ./gradlew checkstyleCheck

- repo: local
  hooks:
    - id: checkstyle-check
      name: Checkstyle check
      entry: ./gradlew checkstyleCheck
      language: system
      files: \.java$
      pass_filenames: false

适用前提:本机需安装 JDK 和 Maven(Maven 项目)或 Gradle(Gradle 项目)。language: system 表示直接在本地环境执行命令,不依赖 pre-commit 的 Python 环境。

关于 pass_filenames: false:Checkstyle 通过构建配置(pom.xml / build.gradle)和 checkstyle.xml 配置文件自行确定检查范围,不需要 pre-commit 传入暂存区文件列表,因此关闭文件名传递,让 Maven/Gradle 自行管理检查范围。

本地与 CI 一致性:此方式确保本地提交前执行的检查命令与 CI 中执行的 mvn checkstyle:check / ./gradlew checkstyleCheck 完全一致,避免"本地通过但 CI 失败"的问题。

增量检查:pre-commit 框架默认只将暂存区中变更的 .java 文件传给 hook。但由于 Checkstyle 的 local hook 使用 pass_filenames: false(让 Maven 自行管理检查范围),Maven 的 checkstyle:check 会对整个项目的 **/*.java 执行全量检查,pre-commit 的文件列表不会被传递给 Maven。

为何不能用 pass_filenames: true + -Dcheckstyle.includes 实现增量:Maven Checkstyle Plugin 的 includes 参数类型为 String,接受的是 Ant 模式(如 **/Foo.java),不是文件路径列表 $TRAE_REF。pre-commit 的 pass_filenames: true 会将暂存区文件路径以空格分隔追加到 entry 命令末尾,但这无法作为 Maven 的 -Dcheckstyle.includes 参数值使用——Maven 期望的是逗号分隔的 Ant 模式,且 pass_filenames 不支持参数格式转换。因此 pass_filenames: true 方案不可行。

可行的方案

  1. 保持 pass_filenames: false,接受全量检查:Maven 的 checkstyle:check 全量检查整个项目源码。对于中小型项目,全量 Checkstyle 检查耗时可控(通常数秒),可直接作为 pre-commit hook 使用。Maven 的 cacheFile 参数可加速重复检查。

  2. Git diff + Checkstyle CLI 直调:绕过 Maven 插件,用 Git diff 获取变更文件后直接调用 Checkstyle CLI:

- repo: local
  hooks:
    - id: checkstyle-incremental
      name: Checkstyle (changed files only)
      entry: bash -c 'files=$(git diff --name-only --diff-filter=ACMR --cached -- "*.java") && [ -n "$files" ] && java -jar checkstyle-10.21.4-all.jar -c checkstyle.xml $files || true'
      language: system
      files: \.java$
      pass_filenames: false

此方案需要本地安装 Checkstyle CLI(checkstyle-10.21.4-all.jar),且不经过 Maven 的配置管理(pom.xml 中的 configLocationsuppressionsLocation 等需手动指定)。适合需要严格文件级增量的场景。

  1. CI 中使用 --from-ref/--to-ref:pre-commit 支持 PR 级增量触发,但由于 pass_filenames: false,实际仍由 Maven 全量执行:
# CI 中只在有 .java 文件变更时触发,但 Maven 仍全量检查
pre-commit run checkstyle-check --from-ref origin/main --to-ref HEAD

综合建议:中小型项目直接使用方案 1(全量检查 + cacheFile 加速);大型项目如需严格文件级增量,使用方案 2(Checkstyle CLI 直调),或改用 Spotless 的 ratchetFrom 实现 Git 级增量。

5.3 IDE 集成

VS Code

安装 Checkstyle for Java 扩展(由 Red Hat 提供),在项目根目录创建 .vscode/settings.json

{
  "java.checkstyle.configuration": "config/checkstyle/checkstyle.xml",
  "java.checkstyle.properties": {}
}

说明:VS Code 的 Checkstyle 集成主要通过 Java 扩展包实现,支持实时高亮显示违规。

IntelliJ IDEA

  1. 安装插件:Settings > Plugins > 搜索 "Checkstyle-IDEA" 并安装
  2. 配置插件:Settings > Tools > Checkstyle
    • 点击 + 添加配置文件,选择 config/checkstyle/checkstyle.xml
    • 勾选 Scan sourcesScan tests
    • 勾选 Suppress errors 可将违规显示为警告而非错误
  3. 实时检查:保存文件时自动执行检查,违规代码会在编辑器中高亮显示

插件仓库https://plugins.jetbrains.com/plugin/1065-checkstyle-idea

Neovim

通过 nvim-lint 插件集成 Checkstyle,提供异步代码检查能力。

5.4 命令行使用方式

# 基本语法
java -jar checkstyle-13.5.0-all.jar -c /path/to/config.xml /path/to/source/code

# 使用 Google Checks 配置检查指定目录
java -jar checkstyle-13.5.0-all.jar -c /google_checks.xml ./src/main/java

# 检查单个文件
java -jar checkstyle-13.5.0-all.jar -c config.xml src/main/java/com/example/MyClass.java

# 生成 XML 格式报告
java -jar checkstyle-13.5.0-all.jar -c config.xml -f xml -o report.xml ./src

# 生成 SARIF 格式报告
java -jar checkstyle-13.5.0-all.jar -c config.xml -f sarif -o report.sarif ./src

# 指定排除的文件/目录
java -jar checkstyle-13.5.0-all.jar -c config.xml -e target/ -e generated/ ./src

# 使用正则表达式排除文件
java -jar checkstyle-13.5.0-all.jar -c config.xml -x ".*[\\/]generated[\\/].*" ./src

下载地址:从 GitHub Releases 下载最新的 checkstyle-X.X.X-all.jar

增量检查 -- 通过分析 Git Diff 获取变更的 Java 文件列表,仅对这些文件执行 Checkstyle 检查:

#!/bin/bash
# 获取相对于 main 分支变更的 Java 文件
PREVIOUS=$(git merge-base origin/main HEAD)
FILES=$(git diff --name-only --diff-filter=ACMR $PREVIOUS HEAD | grep '\.java$')

if [ -n "$FILES" ]; then
  echo "Checking changed files:"
  echo "$FILES"
  java -jar checkstyle-13.5.0-all.jar -c config/checkstyle.xml $FILES
  if [ $? -ne 0 ]; then
    echo "Checkstyle check failed!"
    exit 1
  fi
else
  echo "No Java files changed, skipping Checkstyle."
fi

CI 脚本调用

GitHub Actions

name: CI Checkstyle

on: [push, pull_request]

jobs:
  checkstyle:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: "21"
          distribution: "temurin"

      - name: Cache Maven dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2
          key: maven-${{ hashFiles('**/pom.xml') }}
          restore-keys: maven-

      - name: Run Checkstyle
        run: mvn checkstyle:check

GitLab CI

checkstyle:
  stage: test
  image: maven:3.9-eclipse-temurin-21
  script:
    - mvn checkstyle:check
  artifacts:
    reports:
      checkstyle:
        - target/checkstyle-result.xml

增量检查 -- 按 PR/MR 变更文件触发增量扫描:

GitHub Actions 中的增量检查

name: Incremental Checkstyle

on: [pull_request]

jobs:
  checkstyle:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: "21"
          distribution: "temurin"

      - name: Run Checkstyle on Changed Files
        run: |
          PREVIOUS=$(git merge-base origin/main ${{ github.sha }})
          FILES=$(git diff --name-only --diff-filter=ACMR $PREVIOUS ${{ github.sha }} | grep '\.java$')
          if [ -n "$FILES" ]; then
            echo "Checking files: $FILES"
            java -jar checkstyle-13.5.0-all.jar -c config/checkstyle.xml $FILES
          else
            echo "No Java files changed."
          fi

GitLab CI 中的增量检查

checkstyle-incremental:
  stage: test
  image: maven:3.9-eclipse-temurin-21
  script:
    - |
      FILES=$(git diff --name-only --diff-filter=ACMR $CI_MERGE_REQUEST_DIFF_BASE_SHA $CI_COMMIT_SHA | grep '\.java$')
      if [ -n "$FILES" ]; then
        echo "Checking files: $FILES"
        java -jar checkstyle-13.5.0-all.jar -c config/checkstyle.xml $FILES
      else
        echo "No Java files changed."
      fi
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

5.5 Gradle 集成(build.gradle)

Gradle 内置了 Checkstyle 插件,开箱即用:

plugins {
    id 'checkstyle'
}

checkstyle {
    toolVersion = '13.5.0'
    configFile = file('config/checkstyle/checkstyle.xml')
    configProperties = [
        'suppressionFile': file('config/checkstyle/checkstyle-suppressions.xml').absolutePath,
        'config_loc': file('config/checkstyle').absolutePath
    ]
}

// 可选:自定义检查任务
tasks.withType(Checkstyle).configureEach {
    maxWarnings = 0  // 警告数上限,0 表示不允许任何警告
    ignoreFailures = false  // 是否忽略失败
}

执行命令:

# 检查主源码
./gradlew checkstyleMain

# 检查测试源码
./gradlew checkstyleTest

# 检查所有源码
./gradlew checkstyle

Gradle Checkstyle Plugin 文档:https://docs.gradle.org/current/userguide/checkstyle_plugin.html

6. 告警抑制(屏蔽)方法

Checkstyle 提供了多种灵活的告警抑制方式,从代码级注释到外部配置文件,满足不同场景的需求。官方过滤器文档:https://checkstyle.org/config_filters.html

6.1 通过集成调度工具屏蔽

Maven excludes 参数

通过 maven-checkstyle-pluginexcludes 配置排除特定文件或目录:

<configuration>
    <excludes>**/generated/**,**/model/**,**/target/**</excludes>
</configuration>

Gradle exclude 配置

checkstyle {
    exclude = ['**/generated/**', '**/model/**']
}

6.2 通过工具配置文件屏蔽

SuppressionFilter(外部 XML 文件)

通过外部 XML 文件定义抑制规则,适合对整个文件、特定类或方法进行批量抑制,是最推荐的企业级抑制方式。

官方文档https://checkstyle.org/config_filters.html#SuppressionFilter

配置方式

<module name="Checker">
    <module name="SuppressionFilter">
        <property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
        <property name="optional" value="true"/>
    </module>
</module>

checkstyle-suppressions.xml 示例

<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
        "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
        "https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
    <!-- 按文件路径抑制 -->
    <suppress files=".*[\\/]generated[\\/].*" checks=".*"/>
    <suppress files=".*[\\/]model[\\/].*" checks="MagicNumber"/>

    <!-- 按检查规则抑制特定文件 -->
    <suppress files=".*[\\/]config[\\/].*" checks="JavadocMethod"/>
    <suppress files=".*[\\/]config[\\/].*" checks="JavadocType"/>

    <!-- 按类名抑制 -->
    <suppress checks="LineLength" files="Application\.java"/>

    <!-- 按消息内容抑制 -->
    <suppress checks="Regexp" message="System\.out\.println"/>
</suppressions>

SuppressionSingleFilter(配置文件内联)

直接在配置文件中内联定义单条抑制规则,无需额外的 XML 文件。

官方文档https://checkstyle.org/config_filters.html#SuppressionSingleFilter

<module name="Checker">
    <module name="SuppressionSingleFilter">
        <property name="checks" value="JavadocMethod"/>
        <property name="files" value=".*Test\.java"/>
    </module>
</module>

6.3 通过工具命令行参数屏蔽

通过命令行的 -e(排除路径)和 -x(排除正则表达式)参数屏蔽特定文件或目录:

官方文档https://checkstyle.org/cmdline.html#Excluding_Specific_Files_or_Directories

# 排除特定目录
java -jar checkstyle-13.5.0-all.jar -c config.xml -e target/ -e generated/ ./src

# 使用正则表达式排除
java -jar checkstyle-13.5.0-all.jar -c config.xml -x ".*[\\/]generated[\\/].*" ./src

6.4 通过代码屏蔽

@SuppressWarnings 注解(类/方法级)

通过 Java 标准注解 @SuppressWarnings 抑制特定规则的告警。需要在配置中启用 SuppressWarningsFilter

官方文档https://checkstyle.org/config_filters.html#SuppressWarningsFilter

配置方式

<module name="Checker">
    <module name="SuppressWarningsFilter"/>
    <module name="TreeWalker">
        <!-- 其他检查规则 -->
    </module>
</module>

使用方式

@SuppressWarnings("checkstyle:MagicNumber")
public void calculate() {
    int timeout = 5000; // 抑制魔法数字检查
}

@SuppressWarnings("checkstyle:HiddenField")
public void setName(String name) {  // 抑制参数名与字段名相同检查
    this.name = name;
}

SuppressionCommentFilter(代码块级注释)

通过成对的注释(OFF/ON)来抑制一段代码区域内的所有或特定告警。

官方文档https://checkstyle.org/config_filters.html#SuppressionCommentFilter

配置方式

<module name="TreeWalker">
    <module name="SuppressionCommentFilter">
        <property name="offCommentFormat" value="CHECKSTYLE:OFF"/>
        <property name="onCommentFormat" value="CHECKSTYLE:ON"/>
    </module>
</module>

使用方式

// CHECKSTYLE:OFF
public void legacyMethod() {
    System.out.println("legacy code");  // 不会触发任何告警
    int x = 42;
}
// CHECKSTYLE:ON

SuppressWithNearbyCommentFilter(行级注释)

通过在代码行附近添加特定格式的注释来抑制告警,是最常用的代码级抑制方式。

官方文档https://checkstyle.org/config_filters.html#SuppressWithNearbyCommentFilter

配置方式

<module name="TreeWalker">
    <module name="SuppressWithNearbyCommentFilter">
        <property name="commentFormat" value="CHECKSTYLE OFF (\w+) FOR (\d+) LINES"/>
        <property name="checkFormat" value="$1"/>
        <property name="influenceFormat" value="$2"/>
    </module>
</module>

使用方式

// CHECKSTYLE OFF MagicNumber FOR 3 LINES
int timeout = 5000;
int retryCount = 3;
long interval = 1000L;
// CHECKSTYLE ON

SuppressWithPlainTextCommentFilter(纯文本注释)

使用纯文本格式注释来抑制 Checker 级别的告警(如 RegexpSingleline),适用于 TreeWalker 之外的检查。

官方文档https://checkstyle.org/config_filters.html#SuppressWithPlainTextCommentFilter

6.5 高级抑制方式

SuppressionXpathFilter(XPath 精确抑制)

基于 XPath 表达式进行精确抑制,可以针对特定的 AST 节点进行细粒度控制。

官方文档https://checkstyle.org/config_filters.html#SuppressionXpathFilter

<module name="Checker">
    <module name="SuppressionXpathFilter">
        <property name="file" value="${config_loc}/checkstyle-xpath-suppressions.xml"/>
        <property name="optional" value="true"/>
    </module>
</module>

6.6 抑制方式对比总结

抑制方式 粒度 配置位置 推荐场景
Maven/Gradle excludes 文件/目录 构建配置 排除生成代码、第三方代码
命令行 -e / -x 文件/目录 CLI 参数 临时排除特定路径
SuppressionFilter 文件/类/方法 外部 XML 文件 企业级批量抑制,推荐使用
SuppressionSingleFilter 文件/类/方法 配置文件内联 简单场景,无需额外文件
@SuppressWarnings 单个类/方法 Java 代码中 临时抑制特定规则
SuppressionCommentFilter 代码块(OFF/ON) Java 代码注释中 抑制遗留代码块
SuppressWithNearbyCommentFilter 指定行数范围 Java 代码注释中 抑制少量代码行的特定规则
SuppressionXpathFilter AST 节点级 外部 XML 文件 精确到特定代码结构


← 返回目录 | ← 返回总览