【问题标题】:Ensure a method is called from another?确保从另一个方法调用?
【发布时间】:2020-08-05 21:47:12
【问题描述】:

我有一个层,叫它Service,另一个叫Permission。我想知道我是否可以执行以下规则:

Service 层内的任何公共类的任何公共方法中,其参数列表包含一个名为foo 的参数,断言它调用了来自Permission 层的方法(理想情况下,确保在其他任何事情之前调用它在Service 层内)。

ArchUnit 可以做到这一点吗?

【问题讨论】:

    标签: archunit


    【解决方案1】:

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

    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-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-02
      • 2015-06-16
      • 1970-01-01
      • 2011-09-09
      • 2017-09-27
      相关资源
      最近更新 更多