如何通过ArchUnit规则检查叶子包中的文件(类、接口等)数量不超过N个
如何通过ArchUnit规则检查叶子包中的文件(类、接口等)数量不超过N个
这个需求确实很实用,ArchUnit本身没有直接提供统计包内文件数的API,但我们可以通过自定义条件来实现这个规则,下面一步步来完成:
第一步:明确核心逻辑
我们需要两个关键判断:
- 识别叶子包:也就是没有子包的底层包(如果一个包下还有子包,就跳过,只检查最底层的包)
- 统计每个叶子包下被ArchUnit扫描到的类/接口/枚举等的数量,确保不超过设定的阈值(比如你例子里的8个)
第二步:实现自定义检查条件
我们可以编写一个自定义的ArchCondition来处理包的数量验证,同时在规则里过滤出叶子包:
import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaPackage; import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; import org.junit.jupiter.api.Test; import java.util.Set; import java.util.stream.Collectors; public class PackageSizeArchTest { private static final int MAX_CLASS_COUNT = 8; @Test void leafPackagesShouldNotExceedMaxClassCount() { // 导入你要扫描的类,这里替换成实际的扫描路径 JavaClasses importedClasses = com.tngtech.archunit.core.importer.ClassFileImporter() .importPackages("natural"); // 获取所有包 Set<JavaPackage> allPackages = importedClasses.getPackages(); // 过滤出叶子包:没有子包的包 Set<JavaPackage> leafPackages = allPackages.stream() .filter(package_ -> isLeafPackage(package_, allPackages)) .collect(Collectors.toSet()); // 自定义条件:检查包内类数量不超过阈值 ArchCondition<JavaPackage> notTooManyClasses = new ArchCondition<>("not contain more than " + MAX_CLASS_COUNT + " classes") { @Override public void check(JavaPackage javaPackage, ConditionEvents events) { long classCount = importedClasses.stream() .filter(clazz -> clazz.getPackageName().equals(javaPackage.getName())) .count(); if (classCount > MAX_CLASS_COUNT) { events.add(SimpleConditionEvent.violated(javaPackage, String.format("Package %s contains %d classes, which exceeds the maximum of %d", javaPackage.getName(), classCount, MAX_CLASS_COUNT))); } } }; // 对所有叶子包应用规则检查 com.tngtech.archunit.lang.syntax.ArchRuleDefinition.packages() .that().areIn(leafPackages) .should(notTooManyClasses) .check(importedClasses); } // 判断一个包是否是叶子包:不存在以该包为父包的子包 private boolean isLeafPackage(JavaPackage package_, Set<JavaPackage> allPackages) { String packageName = package_.getName(); return allPackages.stream() .noneMatch(p -> p.getName().startsWith(packageName + ".")); } }
第三步:代码说明
- 叶子包判断:
isLeafPackage方法通过检查所有包中是否存在以当前包为前缀的子包,来确定是否是叶子包 - 类数量统计:在自定义
ArchCondition中,我们通过流过滤出属于当前包的类,统计数量后和阈值对比 - 规则应用:最后把过滤出的叶子包作为规则的目标,应用自定义条件进行检查
注意事项
- 这里的“文件数”其实是ArchUnit扫描到的代码元素数(类、接口、枚举、注解等),如果你的包里有非代码文件(比如配置文件、资源文件),ArchUnit不会统计这些
- 你可以根据实际需求调整
MAX_CLASS_COUNT的值,以及扫描的包路径 - 如果需要更灵活的范围(比如只检查特定前缀的包),可以在过滤叶子包的时候加上额外的条件
备注:内容来源于stack exchange,提问作者PatPanda
相关产品推荐
相关产品推荐

