【发布时间】:2020-08-05 21:47:12
【问题描述】:
我有一个层,叫它Service,另一个叫Permission。我想知道我是否可以执行以下规则:
从Service 层内的任何公共类的任何公共方法中,其参数列表包含一个名为foo 的参数,断言它调用了来自Permission 层的方法(理想情况下,确保在其他任何事情之前调用它在Service 层内)。
ArchUnit 可以做到这一点吗?
【问题讨论】:
标签: archunit
我有一个层,叫它Service,另一个叫Permission。我想知道我是否可以执行以下规则:
从Service 层内的任何公共类的任何公共方法中,其参数列表包含一个名为foo 的参数,断言它调用了来自Permission 层的方法(理想情况下,确保在其他任何事情之前调用它在Service 层内)。
ArchUnit 可以做到这一点吗?
【问题讨论】:
标签: archunit
它不能开箱即用,但您可以通过以下解决方案实现该目标:
DescribedPredicate<JavaClass> isPermissionClass = JavaClass.Predicates
.resideInAnyPackage("..permission..");
classes()
.that().resideInAnyPackage("..service..")
.should(new ContainOnlyMethodsCallingPermissionClass(isPermissionClass));
ContainOnlyMethodsCallingPermissionClass 可以定义如下。它首先收集所有声明的方法,然后检查对权限类的传出方法调用是否覆盖了所有收集的方法。
public static class ContainOnlyMethodsCallingPermissionClass extends ArchCondition<JavaClass> {
private final DescribedPredicate<JavaClass> isPermissionClass;
public ContainOnlyMethodsCallingPermissionClass(DescribedPredicate<JavaClass> isPermissionClass) {
super("only contain methods calling a permission class");
this.isPermissionClass = isPermissionClass;
}
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
Set<String> methodIdentifiers = getMethodIdentifiersOfClass(javaClass);
methodIdentifiers.removeAll(collectMethodsCallingPermissionClass(javaClass));
for (String methodId : methodIdentifiers) {
events.add(new SimpleConditionEvent(javaClass, false, methodId));
}
}
private Set<String> collectMethodsCallingPermissionClass(JavaClass javaClass) {
Set<String> methodIdentifiers = new HashSet<>();
for (JavaAccess<?> access : javaClass.getAccessesFromSelf()) {
if (!isCallFromMethod(access)) {
continue;
}
if (isMethodCallToPermissionClass(access)) {
JavaMethod callingMethod = (JavaMethod) access.getOwner();
methodIdentifiers.remove(callingMethod.getFullName());
}
}
return methodIdentifiers;
}
private Set<String> getMethodIdentifiersOfClass(JavaClass javaClass) {
return javaClass.getMethods().stream().filter(method -> !method.isConstructor())
.map(method -> method.getFullName()).collect(Collectors.toSet());
}
private boolean isCallFromMethod(JavaAccess<?> access) {
return access.getOrigin() instanceof JavaMethod;
}
private boolean isMethodCallToPermissionClass(JavaAccess<?> access) {
return isPermissionClass.apply(access.getTargetOwner());
}
}
您还可以将JavaClasses 上的谓词更改为JavaMethodCalls 上的谓词,以便更准确地定位相关权限方法。这还允许过滤掉服务层中不相关的方法(例如,toString() 方法)。
【讨论】: