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

如何实现检查项目中所有package-info文件的包级注解一致性?

Great question! Checking consistent package-level annotations across all package-info.java files is a common need for maintaining code standards, and it's totally reasonable to feel frustrated that default rules for Checkstyle/PMD/FindBugs don’t cover this out of the box. Here are four practical, actionable ways to implement this check:


1. Custom Checkstyle Rule

Checkstyle is highly extensible, so you can build a custom check tailored to your annotation requirement:

First, create a check class that extends AbstractCheck:

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.AnnotationUtil;

public class RequiredPackageAnnotationCheck extends AbstractCheck {

    private static final String REQUIRED_ANNOTATION = "javax.annotation.ParametersAreNonnullByDefault";
    private static final String ANNOTATION_SIMPLE_NAME = REQUIRED_ANNOTATION.substring(REQUIRED_ANNOTATION.lastIndexOf('.') + 1);

    @Override
    public int[] getDefaultTokens() {
        return new int[]{TokenTypes.PACKAGE_DEF};
    }

    @Override
    public int[] getAcceptableTokens() {
        return getDefaultTokens();
    }

    @Override
    public int[] getRequiredTokens() {
        return getDefaultTokens();
    }

    @Override
    public void visitToken(DetailAST ast) {
        // Verify the package declaration has the required annotation
        boolean hasRequiredAnnotation = AnnotationUtil.containsAnnotation(ast, REQUIRED_ANNOTATION);
        
        if (!hasRequiredAnnotation) {
            log(ast.getLineNo(), "Package must be annotated with @" + ANNOTATION_SIMPLE_NAME);
        }
    }
}

Next:

  • Package this class into a JAR and add it to your Checkstyle classpath
  • Update your Checkstyle config XML to include the new rule:
    <module name="RequiredPackageAnnotationCheck"/>
    

2. Custom PMD Rule

PMD supports both XPath-based and Java-based custom rules. For simplicity, here's an XPath rule that targets missing annotations in package-info.java:

Add this to your PMD ruleset XML:

<rule name="RequiredPackageAnnotation" language="java" message="Package must include @ParametersAreNonnullByDefault">
    <description>Enforces consistent package-level annotations across all package-info.java files</description>
    <priority>3</priority>
    <pattern>
        <![CDATA[
        //PackageDeclaration[not(Annotation/MarkerAnnotation/Name[text()='ParametersAreNonnullByDefault'] or Annotation/NormalAnnotation/Name[text()='ParametersAreNonnullByDefault'])]
        ]]>
    </pattern>
    <example>
        <![CDATA[
        // Bad: Missing required annotation
        package com.example;

        // Good: Annotation and import are present
        @ParametersAreNonnullByDefault
        package com.example;
        import javax.annotation.ParametersAreNonnullByDefault;
        ]]>
    </example>
</rule>

Run PMD with this ruleset, and it will flag any package-info.java files missing the annotation.


3. Gradle/Maven Build Script Check

If you want a lightweight solution without adding static analysis tool customizations, you can add a build task to validate the files directly:

Gradle Example

task checkPackageAnnotations {
    doLast {
        def requiredAnnotation = "@ParametersAreNonnullByDefault"
        def requiredImport = "import javax.annotation.ParametersAreNonnullByDefault;"
        
        fileTree(dir: "src/main/java", include: "**/package-info.java").each { file ->
            def content = file.text
            if (!content.contains(requiredAnnotation) || !content.contains(requiredImport)) {
                throw new GradleException("Invalid package-info file: ${file.path}\nMissing required annotation or import")
            }
        }
    }
}

// Attach to the standard check task so it runs automatically
check.dependsOn(checkPackageAnnotations)

Maven Example

Use the exec-maven-plugin to run a simple script (or a small Java utility class) that checks the files during the verify phase.


4. Custom Java Utility with AST Parsing

For more robust validation (avoiding false positives from text-only matching), use an AST parser like JavaParser to inspect the structure of package-info.java files:

First, add JavaParser as a dependency, then write this utility:

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.PackageDeclaration;
import com.github.javaparser.ast.expr.AnnotationExpr;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class PackageAnnotationChecker {

    private static final String REQUIRED_ANNOTATION = "ParametersAreNonnullByDefault";
    private static final String REQUIRED_IMPORT = "javax.annotation.ParametersAreNonnullByDefault";

    public static void main(String[] args) throws IOException {
        File srcDir = new File("src/main/java");
        scanDirectory(srcDir);
    }

    private static void scanDirectory(File dir) throws IOException {
        File[] files = dir.listFiles();
        if (files == null) return;

        for (File file : files) {
            if (file.isDirectory()) {
                scanDirectory(file);
            } else if (file.getName().equals("package-info.java")) {
                validateFile(file);
            }
        }
    }

    private static void validateFile(File file) throws IOException {
        CompilationUnit cu = JavaParser.parse(new FileInputStream(file));
        
        // Check package declaration exists and has the annotation
        PackageDeclaration pkg = cu.getPackageDeclaration()
                .orElseThrow(() -> new IllegalArgumentException("No package declaration in " + file.getPath()));
        
        boolean hasAnnotation = pkg.getAnnotations().stream()
                .map(AnnotationExpr::getNameAsString)
                .anyMatch(REQUIRED_ANNOTATION::equals);
        
        // Check the required import is present
        boolean hasImport = cu.getImports().stream()
                .map(imp -> imp.getNameAsString())
                .anyMatch(REQUIRED_IMPORT::equals);
        
        if (!hasAnnotation || !hasImport) {
            System.err.println("ERROR: Invalid package-info file - " + file.getPath());
            System.err.println("Missing: " + (!hasAnnotation ? "@" + REQUIRED_ANNOTATION : "") + 
                    (!hasImport ? " " + REQUIRED_IMPORT : ""));
            System.exit(1);
        }
    }
}

You can run this utility manually, or integrate it into your build pipeline.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:08:29