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
}
}
@NotNullis present … on the field … via another annotation” does not exist in Java. Tools processing annotations may implement such a logic but since you’re the one implementing the tool, it’s on you to check whetherMyAnnotationhas been annotated with@NotNullwhich is what actually has been done on the language level and bytecode level. There is no way around reading and parsing the class file ofMyAnnotationto find out whether the@NotNullannotation is present.