如何防止 ArchUnit 对某个 API(例如 Streaming API 或 Builder API)的通用使用?

How to prevent generic usage of a certain API (e.g Streaming API or Builder API) with ArchUnit?

给定一组 classes,其中不应禁止使用某些 APIs(无论出于何种原因)。

例如,禁止 Java 8 Streaming API,或调用 Builder 内部 class 因为你想强制使用特定的循环和构造函数 classes.

关键部分是你事先不知道 ownerName 并且没有 callMethod(methodName) 我可以粗粒度地禁止调用 stream()build() 方法.

有什么想法吗?

您可以使用谓词而不是流利的 API 来编写功能强大的 ArchRule;在你的情况下 .callMethodWhere(DescribedPredicate<? super JavaMethodCall> predicate).

要防止使用 someCollection.stream(),您可以从 HasName.Predicates.name:

开始
noClasses().should().callMethodWhere(name("stream"))

为避免误报,规则可能应该更加具体。

ArchUnit 提供了许多有用的预定义谓词;您只需要找到它们(并可能通过 DescribedPredicate.forSubtype 处理它们的协变参数类型):

noClasses().should().callMethodWhere(target(
    name("stream").<CodeUnitCallTarget>forSubtype().and(
        owner(assignableTo(Collection.class)).<CodeUnitCallTarget>forSubtype().and(
                rawParameterTypes(new Class[0])
        )
    )
))

我个人觉得定义自定义谓词更简单(例如使用 DescribedPredicate.describe):

noClasses().should().callMethodWhere(target(describe("is Collection.stream()",
    target -> "stream".equals(target.getName()) &&
            target.getOwner().isAssignableTo(Collection.class) &&
            target.getParameterTypes().isEmpty()
)))

仅供参考:我的解决方案使用以下导入:

import static com.tngtech.archunit.base.DescribedPredicate.describe;
import static com.tngtech.archunit.core.domain.JavaCall.Predicates.target;
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.assignableTo;
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name;
import static com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With.owner;
import static com.tngtech.archunit.core.domain.properties.HasParameterTypes.Predicates.rawParameterTypes;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;