Lombok充当修改您的类的注释处理器。
在这种情况下@lombok.experimental.UtilityClass
(可能还有其他 lombok 注释),最终的字节码实际上不再包含注释:
@lombok.experimental.UtilityClass
public class App {
static int hd;
}
被编译(转换)为
public final class App
flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
this_class: #5 // App
super_class: #6 // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 1
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Class #16 // java/lang/UnsupportedOperationException
#3 = String #17 // This is a utility class and cannot be instantiated
#4 = Methodref #2.#18 // java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
#5 = Class #19 // App
#6 = Class #20 // java/lang/Object
#7 = Utf8 hd
#8 = Utf8 I
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 SourceFile
#14 = Utf8 App.java
#15 = NameAndType #9:#10 // "<init>":()V
#16 = Utf8 java/lang/UnsupportedOperationException
#17 = Utf8 This is a utility class and cannot be instantiated
#18 = NameAndType #9:#21 // "<init>":(Ljava/lang/String;)V
#19 = Utf8 App
#20 = Utf8 java/lang/Object
#21 = Utf8 (Ljava/lang/String;)V
{
static int hd;
descriptor: I
flags: (0x0008) ACC_STATIC
private App();
descriptor: ()V
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: new #2 // class java/lang/UnsupportedOperationException
7: dup
8: ldc #3 // String This is a utility class and cannot be instantiated
10: invokespecial #4 // Method java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
13: athrow
LineNumberTable:
line 4: 0
}
也可以从这个纯 Java 代码生成:
public final class App {
static int hd;
private App() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}
如果你想用 ArchUnit 检测字节码中的这种模式,你可能不得不对 Lombok 所做的事情进行逆向工程,例如在调用UnsupportedOperationException(String)
构造函数的最终类中搜索私有构造函数:
ArchRule no_UtilityClass = noConstructors()
.should().bePrivate()
.andShould().beDeclaredInClassesThat().haveModifier(JavaModifier.FINAL)
.andShould(new ArchCondition<JavaCodeUnit>("call new UnsupportedOperationException(String)") {
@Override
public void check(JavaCodeUnit codeUnit, ConditionEvents events) {
boolean satisfied = codeUnit.getCallsFromSelf().stream().anyMatch(call ->
call.getTargetOwner().isEquivalentTo(UnsupportedOperationException.class)
&& call.getName().equals(JavaConstructor.CONSTRUCTOR_NAME)
&& call.getTarget().getRawParameterTypes().size() == 1
&& call.getTarget().getRawParameterTypes().get(0).isEquivalentTo(String.class)
);
String message = String.format("%s %s `new UnsupportedOperationException(String)` in %s",
codeUnit.getDescription(), satisfied ? "calls" : "does not call", codeUnit.getSourceCodeLocation()
);
events.add(new SimpleConditionEvent(codeUnit, satisfied, message));
}
});
如果您想禁止lombok.experimental.*
在源代码中使用,很遗憾您需要另一个工具;ArchUnit(目前)只分析bytecode。