当前位置: 首页 > 知识库问答 >
问题:

如何验证方法注释是否使用了具有特定值的属性,使用arch Unit

卫兴邦
2023-03-14

我有一个审计注释,它有许多可选属性,我需要对某些包强制使用一个布尔属性。

我正试图使用archunit来完成此验证,这样每当开发人员提交违反规则的代码时,CI就会打破规则并通知团队。

这将破坏构建:

@Audit
public myMethod(...) {
...
}

这是正确的方法:

@Audit(useAccount = true)
public myMethod(...) {
...
}

问题是Archunit目前不支持对方法进行断言。我本想做一些事情,比如:

methods().that().resideInAnyPackage("..controllers..", "..service..").and().areAnnotatedWith(Audit.class).should(attributeCheckCondition)

然后,我的自定义条件attributeCheckCondition将负责查看属性值。

当我们检索类时,有没有一种方法可以检索方法?不必编写更复杂的谓词和条件?

共有3个答案

葛昕
2023-03-14

除了@raspacorp(谁启发了我!),这里还有另一个自定义示例。

为了检查安全(角色)方法注释,我实现了以下规则:

public static class SecuredByRoleArchCondition extends ArchCondition<JavaMethod> {
    private final String[] expectedRoles;

    public SecuredByRoleArchCondition(String[] expectedRoles) {
        super(String.format("accessed by @Secured methods with roles %s", Arrays.toString(expectedRoles)));
        this.expectedRoles = expectedRoles;
    }

    public static SecuredByRoleArchCondition haveSecuredAnnotationWithRoles(String... expectedRoles) {
        return new SecuredByRoleArchCondition(expectedRoles);
    }

    @Override
    public void check(JavaMethod javaMethod, ConditionEvents events) {
        if (!javaMethod.isAnnotatedWith(Secured.class)) {
            String message = String.format("Method %s annotation @Secured(%s) is missing",
                    javaMethod.getFullName(), Arrays.toString(expectedRoles));
            events.add(SimpleConditionEvent.violated(javaMethod, message));
            return;
        }
        String[] annotationRoleValues = javaMethod.getAnnotationOfType(Secured.class).value();
        if (!Arrays.equals(annotationRoleValues, expectedRoles)) {
            String message = String.format("Method %s @Secured with %s has wrong roles, expected %s instead",
                    javaMethod.getFullName(), Arrays.toString(annotationRoleValues), Arrays.toString(expectedRoles));
            events.add(SimpleConditionEvent.violated(javaMethod, message));
        }
    }
}

以下是此archCondition的示例用法:

@ArchTest
static ArchRule admin_actions_with_post_mapping_should_be_secured_by_ADMIN_WRITE_role =
        methods()
                .that().areDeclaredInClassesThat().resideInAnyPackage(ADMIN_PACKAGES)
                .and().areAnnotatedWith(PostMapping.class)
                .should(haveSecuredAnnotationWithRoles("ADMIN_WRITE"));
韦鸣
2023-03-14

我找到了一种在类上使用自定义谓词和条件的方法,当我这样做时,我没有意识到Roland的反应似乎更好,因为它提供了一种从方法角度表达规则断言的方法,这就是我要求的原因。

然而,我想在这里发布解决方案,以便对其他人有用。

DescribedPredicate<JavaClass> HAVE_A_METHOD_ANNOTATED_WITH_AUDIT =
    new DescribedPredicate<JavaClass>("have a method annotated with @Audit")
    {
        @Override
        public boolean apply(JavaClass input)
        {
            return input.getMethods().stream().anyMatch(method -> method.isAnnotatedWith(Audit.class));
        }
    };

ArchCondition<JavaClass> ONLY_SET_ATTRIBUTE_USE_ACCOUNT_SET_TO_TRUE =
    new ArchCondition<JavaClass>("only set useAccount attribute to true")
    {
        @Override
        public void check(JavaClass item, ConditionEvents events)
        {
            item.getMethods().stream().filter(method ->
           method.isAnnotatedWith(Audit.class) && !method.getAnnotationOfType(Audit.class)
                                                            .useAccount()
            )
                .forEach(method -> {
                    String message = String.format(
                        "Method %s is annotated with @Audit but useAccount is not set to true",
                        method.getFullName());
                    events.add(SimpleConditionEvent.violated(method, message));
                });
        }
    };

然后,规则表示为:

ArchRule ANNOTATION_RULE = classes()
    .that()
    .resideInAnyPackage("..controller..", "..service..")
    .and(HAVE_A_METHOD_ANNOTATED_WITH_AUDIT)
    .should(ONLY_SET_ATTRIBUTE_USE_ACCOUNT_SET_TO_TRUE);
郝池暝
2023-03-14

由于ArchUnit 0.10.0,可以为成员创建规则。

methods().that().areDeclaredInClassesThat().resideInAnyPackage("..controllers..", "..service..").and().areAnnotatedWith(Audit.class).should(attributeCheckCondition)

另请参见《用户手册》中的组成成员规则。

由于目前没有可用于方法的基本规则定义,因此需要中间步骤。ArchUnit有一个类转换器(ClassesTransformer),用于将JavaClass转换为其他类型的集合。

ClassesTransformer<JavaMethod> methods = new AbstractClassesTransformer<JavaMethod>("methods") {
    @Override
    public Iterable<JavaMethod> doTransform(JavaClasses javaClasses) {
        Set<JavaMethod> allMethods = new HashSet<>();
        for (JavaClass javaClass : javaClasses) {
            allMethods.addAll(javaClass.getMethods());
        }
        return allMethods;
    }
};

然后,可以将该类Transformer用作自定义规则定义的基础。

ArchRule rule = ArchRuleDefinition.all(methods).that(owner(resideInAnyPackage("..controllers..", "..service.."))).and(annotatedWith(Audit.class)).should(haveAttributeValue());
rule.check(javaClasses);

另请参阅用户指南和本期中的自定义概念规则。

 类似资料:
  • 假设我有以下课程: 是否可以通过“MyProduct”类验证“code”属性?比如:

  • 我想根据“配置文件”设置注释的值。 让我举个例子来解释; 在上面的例子中,我们可以看到活动的“配置文件”是PROD,但是假设我们想要使用DEV配置文件,我们将不得不注释来自PROD的@Table注释,并取消注释DEV@Table注释。 如果这只针对一个实体,那不会是一个问题,但我有很多实体都有这种情况,所以我不认为这是处理这种即兴“简介”的方式。 你知道有什么办法可以解决这种情况吗?

  • 在我们的组织中,我们有一些微服务和许多库。 有些库定义的“public”类不用于公共用途-仅在多个包中的库内部(因此不能是包私有的) 我想添加一些类似于Kotlin的“内部”修饰符的内容—一个checkstyle规则/注释处理器/测试组件,用于验证使用者应用程序是否不导入这些类。 例如,我将它们标记为@ForInternalUsageOnly或放入包com.ourorg.mylib.interna

  • 我已经使用Spring几十年了,但以前从未遇到过这个用例。 是否有方法注入所有带特定注释的bean,例如,所有带服务的bean或所有带自定义注释的bean? 我唯一的想法是注入上下文,获取所有bean并手动过滤。如果这是唯一的方法,Spring是否公开了一种递归扫描类层次结构以查找(元)注释的方法(因为大多数Spring注释都可以用作元注释)?

  • 我正在尝试通过设置“use.async.http.管道”属性来利用 CXF 异步 HTTP 客户端传输,如本线程中所述,并由这篇 CXF 文章推荐。 我使用以下代码执行此操作: 上面的异常堆栈表明类仍在使用中,并且根据 CXF 文档,该设置尚未生效。 我试图弄清楚的是,如何确保“use.async.http.conduct”生效,即是否存在可以测试的特定行为,或者我可以在客户端上启用的特定日志配置

  • 我需要测试验证注释,但看起来它们不起作用。我不确定JUnit是否也是正确的。目前,测试将通过,但您可以看到指定的电子邮件地址是错误的。 JUnit 待测试类别