Using Java ASM library to manipulate bytecode at build time, how can I check if an annotation on a field contains other specific annotations?

huangapple go评论96阅读模式
英文:

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 是否直接存在于字段上或通过另一个注释存在于字段上?

  1. import jakarta.validation.constraints.NotNull;
  2. import javax.persistence.*;
  3. @Entity
  4. public class SampleClass {
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.AUTO)
  7. private Long id;
  8. @MyAnnotation("John")
  9. public String name;
  10. public Long getId() {
  11. return id;
  12. }
  13. public void setId(Long id) {
  14. this.id = id;
  15. }
  16. }

MyAnnotation.java:

  1. import jakarta.validation.constraints.NotNull;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @NotNull
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.METHOD})
  9. public @interface MyAnnotation {
  10. String value() default "";
  11. }

Gradle 构建脚本:

  1. import org.objectweb.asm.*
  2. import org.objectweb.asm.Opcodes.ASM9
  3. import java.util.*
  4. plugins {
  5. java
  6. }
  7. group "org.example"
  8. version "1.0-SNAPSHOT"
  9. repositories {
  10. mavenCentral()
  11. }
  12. dependencies {
  13. implementation("org.ow2.asm:asm:9.5")
  14. implementation("jakarta.persistence:jakarta.persistence-api:2.2.3")
  15. implementation("jakarta.validation:jakarta.validation-api:3.0.0")
  16. }
  17. buildscript {
  18. repositories {
  19. mavenCentral()
  20. }
  21. dependencies {
  22. classpath("org.ow2.asm:asm:9.5")
  23. classpath("jakarta.persistence:jakarta.persistence-api:2.2.3")
  24. classpath("jakarta.validation:jakarta.validation-api:3.0.0")
  25. }
  26. }
  27. tasks.compileJava {
  28. doLast {
  29. val notNullAnnotation = Type.getType("Ljakarta/validation/constraints/NotNull")
  30. val classReader = ClassReader(file("build/classes/java/main/org/example/SampleClass.class").readBytes())
  31. val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
  32. classReader.accept(object : ClassVisitor(ASM9, classWriter) {
  33. var isEntity = false
  34. override fun visit(
  35. version: Int,
  36. access: Int,
  37. name: String,
  38. signature: String?,
  39. superName: String?,
  40. interfaces: Array<out String>?
  41. ) {
  42. super.visit(version, access, name, signature, superName, interfaces)
  43. }
  44. override fun visitField(
  45. access: Int,
  46. name: String,
  47. descriptor: String,
  48. signature: String?,
  49. value: Any?
  50. ): FieldVisitor {
  51. val fieldVisitor = super.visitField(access, name, descriptor, signature, value)
  52. return object : FieldVisitor(Opcodes.ASM9, fieldVisitor) {
  53. var hasNotNullAnnotation = false
  54. override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor {
  55. val av = super.visitAnnotation(descriptor, visible)
  56. if (descriptor.contains(notNullAnnotation.descriptor)) {
  57. hasNotNullAnnotation = true
  58. }
  59. }
  60. override fun visitEnd() {
  61. System.out.println("hasNotNullAnnotation: " + hasNotNullAnnotation); // always false
  62. super.visitEnd()
  63. }
  64. }
  65. }
  66. }, 0)
  67. file("build/classes/java/main/org/example/SampleClass.class").writeBytes(classWriter.toByteArray())
  68. }
  69. }
  70. val asmConfiguration = project.configurations.getByName("implementation")
  71. tasks.compileJava {
  72. doLast {
  73. asmConfiguration
  74. }
  75. }
英文:

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?

  1. import jakarta.validation.constraints.NotNull;
  2. import javax.persistence.*;
  3. @Entity
  4. public class SampleClass {
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.AUTO)
  7. private Long id;
  8. @MyAnnotation(&quot;John&quot;)
  9. public String name;
  10. public Long getId() {
  11. return id;
  12. }
  13. public void setId(Long id) {
  14. this.id = id;
  15. }
  16. }

MyAnnotation.java:

  1. import jakarta.validation.constraints.NotNull;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @NotNull
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.METHOD})
  9. public @interface MyAnnotation {
  10. String value() default &quot;&quot;;
  11. }

Gradle build script:

  1. import org.objectweb.asm.*
  2. import org.objectweb.asm.Opcodes.ASM9
  3. import java.util.*
  4. plugins {
  5. java
  6. }
  7. group &quot;org.example&quot;
  8. version &quot;1.0-SNAPSHOT&quot;
  9. repositories {
  10. mavenCentral()
  11. }
  12. dependencies {
  13. implementation(&quot;org.ow2.asm:asm:9.5&quot;)
  14. implementation(&quot;jakarta.persistence:jakarta.persistence-api:2.2.3&quot;)
  15. implementation(&quot;jakarta.validation:jakarta.validation-api:3.0.0&quot;)
  16. }
  17. buildscript {
  18. repositories {
  19. mavenCentral()
  20. }
  21. dependencies {
  22. classpath(&quot;org.ow2.asm:asm:9.5&quot;)
  23. classpath(&quot;jakarta.persistence:jakarta.persistence-api:2.2.3&quot;)
  24. classpath(&quot;jakarta.validation:jakarta.validation-api:3.0.0&quot;)
  25. }
  26. }
  27. tasks.compileJava {
  28. doLast {
  29. val notNullAnnotation = Type.getType(&quot;Ljakarta/validation/constraints/NotNull&quot;)
  30. val classReader = ClassReader(file(&quot;build/classes/java/main/org/example/SampleClass.class&quot;).readBytes())
  31. val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
  32. classReader.accept(object : ClassVisitor(ASM9, classWriter) {
  33. var isEntity = false
  34. override fun visit(
  35. version: Int,
  36. access: Int,
  37. name: String,
  38. signature: String?,
  39. superName: String?,
  40. interfaces: Array&lt;out String&gt;?
  41. ) {
  42. super.visit(version, access, name, signature, superName, interfaces)
  43. }
  44. override fun visitField(
  45. access: Int,
  46. name: String,
  47. descriptor: String,
  48. signature: String?,
  49. value: Any?
  50. ): FieldVisitor {
  51. val fieldVisitor = super.visitField(access, name, descriptor, signature, value)
  52. return object : FieldVisitor(Opcodes.ASM9, fieldVisitor) {
  53. var hasNotNullAnnotation = false
  54. override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor {
  55. val av = super.visitAnnotation(descriptor, visible)
  56. if (descriptor.contains(notNullAnnotation.descriptor)) {
  57. hasNotNullAnnotation = true
  58. }
  59. }
  60. override fun visitEnd() {
  61. System.out.println(&quot;hasNotNullAnnotation: &quot; + hasNotNullAnnotation); // always false
  62. super.visitEnd()
  63. }
  64. }
  65. }
  66. }, 0)
  67. file(&quot;build/classes/java/main/org/example/SampleClass.class&quot;).writeBytes(classWriter.toByteArray())
  68. }
  69. }
  70. val asmConfiguration = project.configurations.getByName(&quot;implementation&quot;)
  71. tasks.compileJava {
  72. doLast {
  73. asmConfiguration
  74. }
  75. }

答案1

得分: 1

你的示例基本上不起作用,因为你只检查@NotNull注解是否直接存在于字段上,而没有处理真正的情况,即@NotNull是一个元注解的情况。你可以尝试使用显式的自定义AnnotationVisitor,如下所示:

  1. override fun visitField(
  2. access: Int,
  3. name: String,
  4. descriptor: String,
  5. signature: String?,
  6. value: Any?
  7. ): FieldVisitor {
  8. val fieldVisitor = super.visitField(access, name, descriptor, signature, value)
  9. return object : FieldVisitor(Opcodes.ASM9, fieldVisitor) {
  10. var hasNotNullAnnotation = false
  11. override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor {
  12. val av = super.visitAnnotation(descriptor, visible)
  13. return object : AnnotationVisitor(ASM9, av) {
  14. override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor {
  15. if (descriptor.contains(notNullAnnotation.descriptor)) {
  16. hasNotNullAnnotation = true
  17. }
  18. return this
  19. }
  20. }
  21. }
  22. override fun visitEnd() {
  23. System.out.println("hasNotNullAnnotation: " + hasNotNullAnnotation);
  24. super.visitEnd()
  25. }
  26. }
  27. }
英文:

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:

  1. override fun visitField(
  2. access: Int,
  3. name: String,
  4. descriptor: String,
  5. signature: String?,
  6. value: Any?
  7. ): FieldVisitor {
  8. val fieldVisitor = super.visitField(access, name, descriptor, signature, value)
  9. return object : FieldVisitor(Opcodes.ASM9, fieldVisitor) {
  10. var hasNotNullAnnotation = false
  11. override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor {
  12. val av = super.visitAnnotation(descriptor, visible)
  13. return object : AnnotationVisitor(ASM9, av) {
  14. override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor {
  15. if (descriptor.contains(notNullAnnotation.descriptor)) {
  16. hasNotNullAnnotation = true
  17. }
  18. return this
  19. }
  20. }
  21. }
  22. override fun visitEnd() {
  23. System.out.println(&quot;hasNotNullAnnotation: &quot; + hasNotNullAnnotation);
  24. super.visitEnd()
  25. }
  26. }
  27. }

huangapple
  • 本文由 发表于 2023年7月13日 21:05:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76679708.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定