1

我有一个图层,调用它Service,另一个调用Permission. 我想知道我是否可以强制执行以下规则:

从 layer 内任何公共类的任何公共方法中,Service其参数列表包含一个名为 的参数foo,断言它从Permissionlayer 调用一个方法(理想情况下,确保在Servicelayer 内的任何其他东西之前调用它)。

ArchUnit 有可能吗?

4

1 回答 1

0

它不能开箱即用,但您可以通过以下解决方案实现该目标:

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()方法)。

于 2020-11-18T08:29:11.860 回答