ArchUnit:未检测到泛型的循环依赖

ArchUnit: cyclic dependency for generics not detected

ArchUnit 0.14.1 没有检测用作通用字段参数的类型的循环依赖性。这是 ArchUnit 的限制还是我做错了什么? ,即。 g.:

package com.test.a;
import com.test.b.B;

public class A {
    protected B b;
}
package com.test.b;
import java.util.Optional;
import com.test.a.A;

public class B {
    protected Optional<A> a; // not detected by archunit
    // protected A a2; // detected by archunit
}
@AnalyzeClasses(packages = "com.test")
public class ArchitecturalChecks {

    @ArchTest
    public void testNoCycles(JavaClasses importedClasses) {
        SlicesRuleDefinition.slices().matching("com.test.(*)..").should().beFreeOfCycles().check(importedClasses);
    }

    @ArchTest
    public void testPackageStructure(JavaClasses importedClasses) {
        // @formatter:off
        Architectures.layeredArchitecture()
            .layer("A").definedBy("com.test.a..")
            .layer("B").definedBy("com.test.b..")
            .whereLayer("A").mayNotBeAccessedByAnyLayer()
            .whereLayer("B").mayOnlyBeAccessedByLayers("A")
            .check(importedClasses);
        // @formatter:on
    }

}

ArchUnit 正在通过分析给定的 Java bytecode.

来完成它的工作

泛型是 type erasure 的主题,因此在检查的字节码中不会有类型信息,您的字段将被解释为 Optional<Object> a 因此没有检测到循环。

ArchUnit 目前(从 version 0.16.0) unfortunately not fully support generics 开始,但正在处理中。 (例如,ArchUnit 0.16.0 与以前的版本不同,它已经检测到 class B extends ArrayList<A> 的依赖关系。)

我相信 ArchUnit 的某些未来版本将通过字段的通用类型参数检测依赖关系,因为信息实际存在于字节码中,请参阅javap -v B.class:

public class com.test.b.B
  minor version: 0
  major version: 55
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2                          // com/test/b/B
  super_class: #3                         // java/lang/Object
  interfaces: 0, fields: 1, methods: 1, attributes: 1
Constant pool:
   #1 = Methodref          #3.#14         // java/lang/Object."<init>":()V
   #2 = Class              #15            // com/test/b/B
   #3 = Class              #16            // java/lang/Object
   #4 = Utf8               a
   #5 = Utf8               Ljava/util/Optional;                  // <- erased type of a
   #6 = Utf8               Signature
   #7 = Utf8               Ljava/util/Optional<Lcom/test/a/A;>;  // <- generic type of a
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               SourceFile
  #13 = Utf8               B.java
  #14 = NameAndType        #8:#9          // "<init>":()V
  #15 = Utf8               com/test/b/B
  #16 = Utf8               java/lang/Object
{
  protected java.util.Optional<com.test.a.A> a;
    descriptor: Ljava/util/Optional;
    flags: (0x0004) ACC_PROTECTED
    Signature: #7                           // Ljava/util/Optional<Lcom/test/a/A;>;

  // ...