英文:
Using Java ASM library to manipulate bytecode at build time, how can I check if an annotation on a field contains other specific annotations?
问题
SampleClass
类中有一个字段,该字段被注释为 MyAnnotation
。
我可以使用 ASM
访问者模式检测到 MyAnnotation
是否存在于字段 name
上,但不确定如何检查 @NotNull
是否包含在 MyAnnotation
中。
如何检测 @NotNull
是否直接存在于字段上或通过另一个注释存在于字段上?
import jakarta.validation.constraints.NotNull;
import javax.persistence.*;
@Entity
public class SampleClass {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@MyAnnotation("John")
public String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
MyAnnotation.java
:
import jakarta.validation.constraints.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@NotNull
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.METHOD})
public @interface MyAnnotation {
String value() default "";
}
Gradle 构建脚本:
import org.objectweb.asm.*
import org.objectweb.asm.Opcodes.ASM9
import java.util.*
plugins {
java
}
group "org.example"
version "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation("org.ow2.asm:asm:9.5")
implementation("jakarta.persistence:jakarta.persistence-api:2.2.3")
implementation("jakarta.validation:jakarta.validation-api:3.0.0")
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.ow2.asm:asm:9.5")
classpath("jakarta.persistence:jakarta.persistence-api:2.2.3")
classpath("jakarta.validation:jakarta.validation-api:3.0.0")
}
}
tasks.compileJava {
doLast {
val notNullAnnotation = Type.getType("Ljakarta/validation/constraints/NotNull")
val classReader = ClassReader(file("build/classes/java/main/org/example/SampleClass.class").readBytes())
val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
classReader.accept(object : ClassVisitor(ASM9, classWriter) {
var isEntity = false
override fun visit(
version: Int,
access: Int,
name: String,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
super.visit(version, access, name, signature, superName, interfaces)
}
override fun visitField(
access: Int,
name: String,
descriptor: String,
signature: String?,
value: Any?
): FieldVisitor {
val fieldVisitor = super.visitField(access, name, descriptor, signature, value)
return object : FieldVisitor(Opcodes.ASM9, fieldVisitor) {
var hasNotNullAnnotation = false
override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor {
val av = super.visitAnnotation(descriptor, visible)
if (descriptor.contains(notNullAnnotation.descriptor)) {
hasNotNullAnnotation = true
}
}
override fun visitEnd() {
System.out.println("hasNotNullAnnotation: " + hasNotNullAnnotation); // always false
super.visitEnd()
}
}
}
}, 0)
file("build/classes/java/main/org/example/SampleClass.class").writeBytes(classWriter.toByteArray())
}
}
val asmConfiguration = project.configurations.getByName("implementation")
tasks.compileJava {
doLast {
asmConfiguration
}
}
英文:
SampleClass
has a field, that is annotated with MyAnnotation
.
I can detect that MyAnnotation
is present on the field name
using ASM
visitor pattern, but unsure how to check if @NotNull
is part of MyAnnotation
.
How can I detect if @NotNull
is present either directly on the field or via another annotation?
import jakarta.validation.constraints.NotNull;
import javax.persistence.*;
@Entity
public class SampleClass {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@MyAnnotation("John")
public String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
MyAnnotation.java
:
import jakarta.validation.constraints.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@NotNull
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.METHOD})
public @interface MyAnnotation {
String value() default "";
}
Gradle build script:
import org.objectweb.asm.*
import org.objectweb.asm.Opcodes.ASM9
import java.util.*
plugins {
java
}
group "org.example"
version "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation("org.ow2.asm:asm:9.5")
implementation("jakarta.persistence:jakarta.persistence-api:2.2.3")
implementation("jakarta.validation:jakarta.validation-api:3.0.0")
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.ow2.asm:asm:9.5")
classpath("jakarta.persistence:jakarta.persistence-api:2.2.3")
classpath("jakarta.validation:jakarta.validation-api:3.0.0")
}
}
tasks.compileJava {
doLast {
val notNullAnnotation = Type.getType("Ljakarta/validation/constraints/NotNull")
val classReader = ClassReader(file("build/classes/java/main/org/example/SampleClass.class").readBytes())
val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
classReader.accept(object : ClassVisitor(ASM9, classWriter) {
var isEntity = false
override fun visit(
version: Int,
access: Int,
name: String,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
super.visit(version, access, name, signature, superName, interfaces)
}
override fun visitField(
access: Int,
name: String,
descriptor: String,
signature: String?,
value: Any?
): FieldVisitor {
val fieldVisitor = super.visitField(access, name, descriptor, signature, value)
return object : FieldVisitor(Opcodes.ASM9, fieldVisitor) {
var hasNotNullAnnotation = false
override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor {
val av = super.visitAnnotation(descriptor, visible)
if (descriptor.contains(notNullAnnotation.descriptor)) {
hasNotNullAnnotation = true
}
}
override fun visitEnd() {
System.out.println("hasNotNullAnnotation: " + hasNotNullAnnotation); // always false
super.visitEnd()
}
}
}
}, 0)
file("build/classes/java/main/org/example/SampleClass.class").writeBytes(classWriter.toByteArray())
}
}
val asmConfiguration = project.configurations.getByName("implementation")
tasks.compileJava {
doLast {
asmConfiguration
}
}
答案1
得分: 1
你的示例基本上不起作用,因为你只检查@NotNull注解是否直接存在于字段上,而没有处理真正的情况,即@NotNull是一个元注解的情况。你可以尝试使用显式的自定义AnnotationVisitor
,如下所示:
override fun visitField(
access: Int,
name: String,
descriptor: String,
signature: String?,
value: Any?
): FieldVisitor {
val fieldVisitor = super.visitField(access, name, descriptor, signature, value)
return object : FieldVisitor(Opcodes.ASM9, fieldVisitor) {
var hasNotNullAnnotation = false
override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor {
val av = super.visitAnnotation(descriptor, visible)
return object : AnnotationVisitor(ASM9, av) {
override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor {
if (descriptor.contains(notNullAnnotation.descriptor)) {
hasNotNullAnnotation = true
}
return this
}
}
}
override fun visitEnd() {
System.out.println("hasNotNullAnnotation: " + hasNotNullAnnotation);
super.visitEnd()
}
}
}
英文:
Your example basically doesn't work because you're only checking if the @NotNull annotation is directly present on the field, but not handling the real case, where @NotNull is a meta-annotation. You might try to use the explicit custom AnnotationVisitor
instead:
override fun visitField(
access: Int,
name: String,
descriptor: String,
signature: String?,
value: Any?
): FieldVisitor {
val fieldVisitor = super.visitField(access, name, descriptor, signature, value)
return object : FieldVisitor(Opcodes.ASM9, fieldVisitor) {
var hasNotNullAnnotation = false
override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor {
val av = super.visitAnnotation(descriptor, visible)
return object : AnnotationVisitor(ASM9, av) {
override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor {
if (descriptor.contains(notNullAnnotation.descriptor)) {
hasNotNullAnnotation = true
}
return this
}
}
}
override fun visitEnd() {
System.out.println("hasNotNullAnnotation: " + hasNotNullAnnotation);
super.visitEnd()
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论