ArchUnit如何限制实现类仅可访问自身实现的接口
ArchUnit实现类访问接口限制方案
方案1:基于命名约定实现(匹配你提到的类名关联逻辑)
如果遵循实现类名+I的接口命名规则(如A对应AI、B对应BI),可以直接通过自定义条件获取当前实现类名称,校验依赖接口的名称是否匹配:
import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; import java.util.stream.Collectors; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; ArchRule implAccessRuleByNaming = classes() .that().resideInAPackage("com.impls..") .should().implement(JavaClass.Predicates.resideInAPackage("com.interfaces..")) .andShould(new ArchCondition<JavaClass>("仅能访问名称包含自身类名的com.interfaces包接口") { @Override public void check(JavaClass implClass, ConditionEvents events) { String implSimpleName = implClass.getSimpleName(); // 仅校验com.interfaces包下的接口依赖,跳过其他类型的依赖 for (JavaClass dependency : implClass.getDependenciesFromSelf()) { if (dependency.isInterface() && dependency.residesInAPackage("com.interfaces..")) { if (!dependency.getSimpleName().contains(implSimpleName)) { String msg = String.format("实现类[%s]违规访问未实现接口[%s]", implClass.getFullName(), dependency.getFullName()); events.add(SimpleConditionEvent.violated(implClass, msg)); } } } } });
方案2:无命名依赖通用实现(直接校验实现关系)
不需要依赖命名约定,直接校验实现类依赖的接口是否是自身已经实现的接口,适配性更强:
import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; import java.util.Set; import java.util.stream.Collectors; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; ArchRule implAccessRule = classes() .that().resideInAPackage("com.impls..") .should().implement(JavaClass.Predicates.resideInAPackage("com.interfaces..")) .andShould(new ArchCondition<JavaClass>("仅能访问自身实现的com.interfaces包接口") { @Override public void check(JavaClass implClass, ConditionEvents events) { // 收集当前类所有允许访问的接口(自身实现的com.interfaces包下接口) Set<String> allowedItfNames = implClass.getAllInterfaces().stream() .filter(itf -> itf.residesInAPackage("com.interfaces..")) .map(JavaClass::getFullName) .collect(Collectors.toSet()); // 校验所有依赖 for (JavaClass dependency : implClass.getDependenciesFromSelf()) { if (dependency.isInterface() && dependency.residesInAPackage("com.interfaces..")) { if (!allowedItfNames.contains(dependency.getFullName())) { String msg = String.format("实现类[%s]违规访问未实现接口[%s],允许访问的接口为:%s", implClass.getFullName(), dependency.getFullName(), allowedItfNames); events.add(SimpleConditionEvent.violated(implClass, msg)); } } } } });
规则说明
- 两种方案均仅限制
com.impls包下的实现类的接口访问行为,不会影响其他包对com.interfaces下接口的正常调用 - 自动过滤了非
com.interfaces包的依赖校验,不影响实现类依赖JDK类、Spring组件、业务工具类等其他逻辑
内容的提问来源于stack exchange,提问作者AHH
相关产品推荐
相关产品推荐

