ArchUnit 可校验包内类是否标注特定自定义注解,需确保注解声明为 @Retention(RUNTIME),通过 classes().that().resideInAPackage().should().beAnnotatedWith() 编写测试,并支持接口限定、排除测试类、存在性检查及反射校验注解属性值。

ArchUnit 可以轻松校验包内类是否标注了特定自定义注解,核心是用 classes() 选择目标类,再通过 haveAnnotation(...) 或 areAnnotatedWith(...) 断言注解存在性。
定义你的自定义注解
确保注解声明时保留运行时信息,否则 ArchUnit 无法在字节码中读取:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DomainService {
}
编写 ArchUnit 测试校验指定包下所有类都标注该注解
例如要求 com.example.app.domain.service 包下的所有类必须标注 @DomainService:
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
import org.junit.jupiter.api.Test;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
class ArchitectureTest {
private final ClassFileImporter importer = new ClassFileImporter();
@Test
void domain_service_classes_must_be_annotated_with_DomainService() {
ArchRule rule = classes()
.that().resideInAPackage("..domain.service..")
.should().beAnnotatedWith(DomainService.class);
rule.check(importer.importPackages("com.example.app"));
}
}
校验“至少有一个类”或“不允许有类”标注注解
灵活组合条件,比如只允许接口标注、或排除测试类:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 仅接口需标注:
that().areInterfaces().and().resideInAPackage("..service..") - 排除测试类:
that().areNotAnnotatedWith(Test.class).and().resideInAPackage("..service..") - 要求至少一个类标注(存在性检查):
classes().that().resideInAPackage("..config..").should().haveAnnotation(Configuration.class)
校验注解属性值(进阶)
ArchUnit 原生不直接校验注解属性,但可通过反射 + ArchCondition 实现。例如检查 @Component(value = "xxx") 的 value 是否非空:
ArchRule rule = classes()
.that().areAnnotatedWith(Component.class)
.should(new ArchCondition<JavaClass>("have non-empty Component.value") {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
Annotation component = javaClass.getAnnotationOfType(Component.class);
String value = (String) component.getMemberValue("value");
boolean ok = value != null && !value.trim().isEmpty();
events.add(new SimpleConditionEvent(javaClass, ok,
javaClass.getSimpleName() + " has empty Component.value"));
}
});
不复杂但容易忽略:务必确认注解的 @Retention(RUNTIME) 和测试扫描路径覆盖目标类。

















