我可以检查 lombok.experimental.* 注释在 ArchUnit 中的使用情况吗?”
Can I check for usage of lombok.experimental.* annotations with ArchUnit?"
如问题所示,我如何使用 archUnit 检查某些导入。
所以我希望测试失败,当被测试的 class 本身导入 lombok.experimental.*.
我知道如何检查包和类似的东西,但这种方法似乎不适用于导入。有什么建议吗?
我的代码:
package com.nikita.Nikitos;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
public class AppTest
{
@Test
public void keineKlassenAusLombokExperimental() {
JavaClasses classes = new ClassFileImporter()
.importPackages("com.nikita..");
noClasses().should().dependOnClassesThat()
.resideInAPackage("lombok.experimental..").check(classes);
}
}
我要测试的class:
package com.nikita.Nikitos;
import lombok.experimental.UtilityClass;
@UtilityClass
public class App
{
static int hd;
}
导入不会在字节码中生成任何签名,因此 ArchUnit 无法直接检测到它。
是否足以检查您的代码是否不依赖于该包?
ArchRule lombok_experimental_is_not_used = noClasses()
.should().dependOnClassesThat().resideInAPackage("lombok.experimental..");
如果你只想检测明星导入,那么很遗憾你需要使用其他工具。
Lombok 充当修改您的 class.
的注释处理器
在 @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 检测字节码中的这种模式,你可能必须 reverse-engineer Lombok 做什么,例如在调用 UnsupportedOperationException(String)
构造函数的最终 classes 中搜索私有构造函数:
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.
如问题所示,我如何使用 archUnit 检查某些导入。
所以我希望测试失败,当被测试的 class 本身导入 lombok.experimental.*.
我知道如何检查包和类似的东西,但这种方法似乎不适用于导入。有什么建议吗?
我的代码:
package com.nikita.Nikitos;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
public class AppTest
{
@Test
public void keineKlassenAusLombokExperimental() {
JavaClasses classes = new ClassFileImporter()
.importPackages("com.nikita..");
noClasses().should().dependOnClassesThat()
.resideInAPackage("lombok.experimental..").check(classes);
}
}
我要测试的class:
package com.nikita.Nikitos;
import lombok.experimental.UtilityClass;
@UtilityClass
public class App
{
static int hd;
}
导入不会在字节码中生成任何签名,因此 ArchUnit 无法直接检测到它。
是否足以检查您的代码是否不依赖于该包?
ArchRule lombok_experimental_is_not_used = noClasses()
.should().dependOnClassesThat().resideInAPackage("lombok.experimental..");
如果你只想检测明星导入,那么很遗憾你需要使用其他工具。
Lombok 充当修改您的 class.
的注释处理器在 @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 检测字节码中的这种模式,你可能必须 reverse-engineer Lombok 做什么,例如在调用 UnsupportedOperationException(String)
构造函数的最终 classes 中搜索私有构造函数:
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.