如何编写Java Checkstyle检查:验证类后缀与所属包的匹配性
编写Checkstyle规则验证类名后缀与包的匹配关系
核心思路
自定义Checkstyle检查类,继承AbstractCheck,针对类定义节点(CLASS_DEF)提取类名和所属包名,通过预定义的类名后缀-包名后缀映射验证是否匹配:
- 提取类名的后缀部分,匹配对应的包名后缀规则
- 若包名不符合对应规则,抛出违规提示
具体实现步骤
1. 自定义检查类代码
创建Java类,实现Checkstyle的检查逻辑:
import com.puppycrawl.tools.checkstyle.api.AbstractCheck; import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.TokenTypes; import com.puppycrawl.tools.checkstyle.utils.CommonUtil; import java.util.HashMap; import java.util.Map; public class SuffixToPackageCheck extends AbstractCheck { // 配置类名后缀与包名后缀的映射,可通过配置文件修改 private Map<String, String> suffixPackageMap = new HashMap<>(); public void setSuffixPackageMap(Map<String, String> suffixPackageMap) { this.suffixPackageMap = suffixPackageMap; } @Override public int[] getDefaultTokens() { return new int[]{TokenTypes.CLASS_DEF}; } @Override public int[] getAcceptableTokens() { return getDefaultTokens(); } @Override public int[] getRequiredTokens() { return getDefaultTokens(); } @Override public void visitToken(DetailAST ast) { // 获取类名 DetailAST classNameAst = ast.findFirstToken(TokenTypes.IDENT); String className = classNameAst.getText(); // 获取包名 String packageName = getCurrentPackageName(); if (CommonUtil.isBlank(packageName)) { return; // 无包名的类跳过检查 } // 遍历映射,检查类名后缀是否匹配 for (Map.Entry<String, String> entry : suffixPackageMap.entrySet()) { String classSuffix = entry.getKey(); String requiredPackageSuffix = entry.getValue(); if (className.endsWith(classSuffix)) { if (!packageName.endsWith(requiredPackageSuffix)) { log(classNameAst.getLineNo(), "类名以{}结尾的类必须位于以{}结尾的包中", classSuffix, requiredPackageSuffix); } // 一个类匹配一个后缀规则即可,无需继续检查 return; } } } private String getCurrentPackageName() { DetailAST packageAst = getRootAST().findFirstToken(TokenTypes.PACKAGE_DEF); if (packageAst == null) { return ""; } DetailAST packageIdent = packageAst.findFirstToken(TokenTypes.DOT); if (packageIdent == null) { return packageAst.findFirstToken(TokenTypes.IDENT).getText(); } return packageIdent.getText(); } }
2. 配置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"> <module name="Checker"> <module name="TreeWalker"> <!-- 注册自定义检查类 --> <module name="com.yourpackage.SuffixToPackageCheck"> <!-- 配置映射:类名后缀 -> 要求的包名后缀 --> <property name="suffixPackageMap"> <map> <entry key="Utils" value=".util"/> <entry key="Service" value=".service"/> <entry key="Repository" value=".repository"/> </map> </property> </module> </module> </module>
3. 测试验证
- 合法示例:
DateTimeUtils类位于com.example.util包下,不会触发违规 - 违规示例:
DateTimeUtils类位于com.example.common包下,会抛出类似如下提示:类名以Utils结尾的类必须位于以.util结尾的包中
注意事项
- 若需要支持正则表达式匹配后缀,可修改
endsWith为正则匹配逻辑 - 确保自定义检查类在Checkstyle的类路径中,否则配置文件会无法加载
- 可通过配置文件灵活扩展更多后缀与包的映射规则
内容的提问来源于stack exchange,提问作者mdenci
相关产品推荐
相关产品推荐

