寻找带有注解的匿名类

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

Find anonymous classes by annotation

问题

有没有办法通过某个注解在一些 Java 反射库(如 Reflections)中找到匿名类?

我有以下代码:
它使用内部类(扩展自Object),并使用@DemoAnnotation进行了注解

  1. public class DemoUsageService {
  2. public void doSomething() {
  3. this.test(new @DemoAnnotation(value="Test") Object() {
  4. String content = "myContent";
  5. });
  6. }
  7. }
  1. @Retention(RUNTIME)
  2. @Target({ElementType.TYPE_USE})
  3. public @interface DemoAnnotation {
  4. String value();
  5. }

现在我想要找到项目中所有使用@DemoAnnotation注解的(匿名)类。


我尝试过使用 Reflections 库:但似乎无法找到匿名类(可以找到内部静态类)。

  1. @Test
  2. public void testFindAnnotatedClasses() throws Exception {
  3. Reflections reflections = new Reflections(
  4. new ConfigurationBuilder()
  5. .setUrls(ClasspathHelper.forPackage(DemoUsageService.class.getPackageName()))
  6. .setScanners(
  7. new SubTypesScanner(false),
  8. new TypeAnnotationsScanner()));
  9. Set<Class<?>> result = reflections.getTypesAnnotatedWith(DemoAnnotation.class);
  10. assertEquals(1, result.size()); //fails, because result.size == 0
  11. //...
  12. }
  1. <dependency>
  2. <groupId>org.reflections</groupId>
  3. <artifactId>reflections</artifactId>
  4. <version>0.9.12</version>
  5. </dependency>

@chrylis -cautiouslyoptimistic:DemoUsageService$1的 javap 反编译器输出 看起来注解在其中。

  1. Classfile /C:/Users/engelmann/git/experiment-anonymousclass-annotationscanning/target/classes/DemoUsageService$1.class
  2. Last modified 21.09.2020; size 1016 bytes
  3. MD5 checksum 2dcb03fe361641b6e04fbb62bbb6c971
  4. Compiled from "DemoUsageService.java"
  5. class DemoUsageService$1
  6. minor version: 0
  7. major version: 55
  8. flags: (0x0020) ACC_SUPER
  9. this_class: #5 // DemoUsageService$1
  10. super_class: #6 // java/lang/Object
  11. interfaces: 0, fields: 2, methods: 1, attributes: 5
  12. Constant pool:
  13. #1 = Fieldref #5.#30 // DemoUsageService$1.this$0:LDemoUsageService;
  14. #2 = Methodref #6.#31 // java/lang/Object."<init>":()V
  15. #3 = String #32 // myContent
  16. #4 = Fieldref #6.#33 // java/lang/Object.content:Ljava/lang/String;
  17. #5 = Class #34 // DemoUsageService$1
  18. #6 = Class #35 // java/lang/Object
  19. #7 = Utf8 content
  20. #8 = Utf8 Ljava/lang/String;
  21. #9 = Utf8 this$0
  22. #10 = Utf8 LDemoUsageService;
  23. #11 = Utf8 "<init>"
  24. #12 = Utf8 (LDemoUsageService;)V
  25. #13 = Utf8 Code
  26. #14 = Utf8 LineNumberTable
  27. #15 = Utf8 LocalVariableTable
  28. #16 = Utf8 this
  29. #17 = Utf8 InnerClasses
  30. #18 = Utf8 LDemoUsageService$1;
  31. #19 = Utf8 MethodParameters
  32. #20 = Utf8 SourceFile
  33. #21 = Utf8 DemoUsageService.java
  34. #22 = Utf8 RuntimeVisibleTypeAnnotations
  35. #23 = Utf8 LDemoAnnotation;
  36. #24 = Utf8 value
  37. #25 = Utf8 Test
  38. #26 = Utf8 EnclosingMethod
  39. #27 = Class #36 // DemoUsageService
  40. #28 = NameAndType #37:#38 // doSomething:()V
  41. #29 = Utf8 NestHost
  42. #30 = NameAndType #9:#10 // this$0:LDemoUsageService;
  43. #31 = NameAndType #11:#38 // "<init>":()V
  44. #32 = Utf8 myContent
  45. #33 = NameAndType #7:#8 // content:Ljava/lang/String;
  46. #34 = Utf8 DemoUsageService$1
  47. #35 = Utf8 java/lang/Object
  48. #36 = Utf8 DemoUsageService
  49. #37 = Utf8 doSomething
  50. #38 = Utf8 ()V
  51. {
  52. java.lang.String content;
  53. descriptor: Ljava/lang/String;
  54. flags: (0x0000)
  55. final DemoUsageService this$0;
  56. descriptor: LDemoUsageService;
  57. flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
  58. DemoUsageService$1(DemoUsageService);
  59. descriptor: (LDemoUsageService;)V
  60. flags: (0x0000)
  61. Code:
  62. stack=2, locals=2, args_size=2
  63. 0: aload_0
  64. 1: aload_1
  65. 2: putfield #1 // Field this$0:LDemoUsageService;
  66. 5: aload_0
  67. 6: invokespecial #2 // Method java/lang/Object."<init>":()V
  68. 9: aload_0
  69. 10: ldc #3 // String myContent
  70. 12: putfield #4 // Field java/lang/Object.content:Ljava/lang/String;
  71. 15: return
  72. LineNumberTable:
  73. line 7: 0
  74. line 8: 9
  75. LocalVariableTable:
  76. Start Length Slot Name Signature
  77. 0 16 0 this LDemoUsageService$1;
  78. 0 16 1 this$0 LDemoUsageService;
  79. MethodParameters:
  80. Name Flags
  81. this$0 final mandated
  82. }
  83. SourceFile: "DemoUsageService.java"
  84. RuntimeVisibleTypeAnnotations:
  85. 0: #23(#24=s#25): CLASS_EXTENDS, type_index=65535
  86. DemoAnnotation(
  87. value="Test"
  88. )
  89. EnclosingMethod: #27.#28 // DemoUsageService.doSomething
  90. NestHost: class DemoUsageService
  91. InnerClasses:
  92. #5; // class DemoUsageService$1
英文:

Is there a way to find anonymous classes by some annotation with some java reflection library like (Reflections)?

I have this code:
it use declare a inner class (extends Object) and annotated it with @DemoAnnotation

  1. public class DemoUsageService {
  2. public void doSomething() {
  3. this.test(new @DemoAnnotation(value=&quot;Test&quot;) Object() {
  4. String content = &quot;myContent&quot;;
  5. });
  6. }
  7. }
  1. @Retention(RUNTIME)
  2. @Target({ElementType.TYPE_USE})
  3. public @interface DemoAnnotation {
  4. String value();
  5. }

Now I want to find all (anonymous) classes in my project that are annotated with @DemoAnnotation.


I tried the Reflections Library: but it seams not to find anonymous classes (inner static classes are found).

  1. @Test
  2. public void testFindAnnotatedClasses() throws Exception {
  3. Reflections reflections = new Reflections(
  4. new ConfigurationBuilder()
  5. .setUrls(ClasspathHelper.forPackage(DemoUsageService.class.getPackageName()))
  6. .setScanners(
  7. new SubTypesScanner(false),
  8. new TypeAnnotationsScanner()));
  9. Set&lt;Class&lt;?&gt;&gt; result = reflections.getTypesAnnotatedWith(DemoAnnotation.class);
  10. assertEquals(1, result.size()); //fails, because result.size == 0
  11. //...
  12. }
  1. &lt;dependency&gt;
  2. &lt;groupId&gt;org.reflections&lt;/groupId&gt;
  3. &lt;artifactId&gt;reflections&lt;/artifactId&gt;
  4. &lt;version&gt;0.9.12&lt;/version&gt;
  5. &lt;/dependency&gt;

@chrylis -cautiouslyoptimistic: The javap decompiler output for DemoUsageService$1 looks like the annotation in there.

  1. Classfile /C:/Users/engelmann/git/experiment-anonymousclass-annotationscanning/target/classes/DemoUsageService$1.class
  2. Last modified 21.09.2020; size 1016 bytes
  3. MD5 checksum 2dcb03fe361641b6e04fbb62bbb6c971
  4. Compiled from &quot;DemoUsageService.java&quot;
  5. class DemoUsageService$1
  6. minor version: 0
  7. major version: 55
  8. flags: (0x0020) ACC_SUPER
  9. this_class: #5 // DemoUsageService$1
  10. super_class: #6 // java/lang/Object
  11. interfaces: 0, fields: 2, methods: 1, attributes: 5
  12. Constant pool:
  13. #1 = Fieldref #5.#30 // DemoUsageService$1.this$0:LDemoUsageService;
  14. #2 = Methodref #6.#31 // java/lang/Object.&quot;&lt;init&gt;&quot;:()V
  15. #3 = String #32 // myContent
  16. #4 = Fieldref #6.#33 // java/lang/Object.content:Ljava/lang/String;
  17. #5 = Class #34 // DemoUsageService$1
  18. #6 = Class #35 // java/lang/Object
  19. #7 = Utf8 content
  20. #8 = Utf8 Ljava/lang/String;
  21. #9 = Utf8 this$0
  22. #10 = Utf8 LDemoUsageService;
  23. #11 = Utf8 &lt;init&gt;
  24. #12 = Utf8 (LDemoUsageService;)V
  25. #13 = Utf8 Code
  26. #14 = Utf8 LineNumberTable
  27. #15 = Utf8 LocalVariableTable
  28. #16 = Utf8 this
  29. #17 = Utf8 InnerClasses
  30. #18 = Utf8 LDemoUsageService$1;
  31. #19 = Utf8 MethodParameters
  32. #20 = Utf8 SourceFile
  33. #21 = Utf8 DemoUsageService.java
  34. #22 = Utf8 RuntimeVisibleTypeAnnotations
  35. #23 = Utf8 LDemoAnnotation;
  36. #24 = Utf8 value
  37. #25 = Utf8 Test
  38. #26 = Utf8 EnclosingMethod
  39. #27 = Class #36 // DemoUsageService
  40. #28 = NameAndType #37:#38 // doSomething:()V
  41. #29 = Utf8 NestHost
  42. #30 = NameAndType #9:#10 // this$0:LDemoUsageService;
  43. #31 = NameAndType #11:#38 // &quot;&lt;init&gt;&quot;:()V
  44. #32 = Utf8 myContent
  45. #33 = NameAndType #7:#8 // content:Ljava/lang/String;
  46. #34 = Utf8 DemoUsageService$1
  47. #35 = Utf8 java/lang/Object
  48. #36 = Utf8 DemoUsageService
  49. #37 = Utf8 doSomething
  50. #38 = Utf8 ()V
  51. {
  52. java.lang.String content;
  53. descriptor: Ljava/lang/String;
  54. flags: (0x0000)
  55. final DemoUsageService this$0;
  56. descriptor: LDemoUsageService;
  57. flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
  58. DemoUsageService$1(DemoUsageService);
  59. descriptor: (LDemoUsageService;)V
  60. flags: (0x0000)
  61. Code:
  62. stack=2, locals=2, args_size=2
  63. 0: aload_0
  64. 1: aload_1
  65. 2: putfield #1 // Field this$0:LDemoUsageService;
  66. 5: aload_0
  67. 6: invokespecial #2 // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V
  68. 9: aload_0
  69. 10: ldc #3 // String myContent
  70. 12: putfield #4 // Field java/lang/Object.content:Ljava/lang/String;
  71. 15: return
  72. LineNumberTable:
  73. line 7: 0
  74. line 8: 9
  75. LocalVariableTable:
  76. Start Length Slot Name Signature
  77. 0 16 0 this LDemoUsageService$1;
  78. 0 16 1 this$0 LDemoUsageService;
  79. MethodParameters:
  80. Name Flags
  81. this$0 final mandated
  82. }
  83. SourceFile: &quot;DemoUsageService.java&quot;
  84. RuntimeVisibleTypeAnnotations:
  85. 0: #23(#24=s#25): CLASS_EXTENDS, type_index=65535
  86. DemoAnnotation(
  87. value=&quot;Test&quot;
  88. )
  89. EnclosingMethod: #27.#28 // DemoUsageService.doSomething
  90. NestHost: class DemoUsageService
  91. InnerClasses:
  92. #5; // class DemoUsageService$1

答案1

得分: 2

我以前从未使用过这个库,但在源代码中稍微查找了一下,似乎我可以使其工作(代码编写得很好,所以我很幸运)。我不知道是否有更好的方法,但是你可以看看以下代码:

  1. static class TestScanner extends AbstractScanner {
  2. @Override
  3. public void scan(Object cls, Store store) {
  4. String className = getMetadataAdapter().getClassName(cls);
  5. try {
  6. Class<?> c = Class.forName(className);
  7. if (c.isAnonymousClass()) {
  8. for (Annotation ann : c.getAnnotatedSuperclass().getAnnotations()) {
  9. store.put(Utils.index(TypeAnnotationsScanner.class), ann.annotationType().getName(), className);
  10. }
  11. }
  12. } catch (ClassNotFoundException e) {
  13. throw new RuntimeException(e);
  14. }
  15. }
  16. }

用法示例:

  1. Reflections reflections = new Reflections(
  2. new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage(DemoUsageService.class.getPackageName()))
  3. .setScanners(new TestScanner(), new SubTypesScanner(false), new TypeAnnotationsScanner()));
  4. Set<Class<?>> result = reflections.getTypesAnnotatedWith(DemoAnnotation.class);
  5. result.forEach(x -> System.out.println(x.getName()));
英文:

I never used this library before, but some poking here and there in the source code and it seems I can make it work (it is written in a nice fashion - so I was lucky, pretty much). I have no idea if there are better ways, but here you go:

  1. static class TestScanner extends AbstractScanner {
  2. @Override
  3. public void scan(Object cls, Store store) {
  4. String className = getMetadataAdapter().getClassName(cls);
  5. try {
  6. Class&lt;?&gt; c = Class.forName(className);
  7. if (c.isAnonymousClass()) {
  8. for (Annotation ann : c.getAnnotatedSuperclass().getAnnotations()) {
  9. store.put(Utils.index(TypeAnnotationsScanner.class), ann.annotationType().getName(), className);
  10. }
  11. }
  12. } catch (ClassNotFoundException e) {
  13. throw new RuntimeException(e);
  14. }
  15. }
  16. }

And usage like:

  1. Reflections reflections = new Reflections(
  2. new ConfigurationBuilder().setUrls(ClasspathHelper.forPackage(DemoUsageService.class.getPackageName()))
  3. .setScanners(new TestScanner(), new SubTypesScanner(false), new TypeAnnotationsScanner()));
  4. Set&lt;Class&lt;?&gt;&gt; result = reflections.getTypesAnnotatedWith(DemoAnnotation.class);
  5. result.forEach(x -&gt; System.out.println(x.getName()));

huangapple
  • 本文由 发表于 2020年9月22日 00:22:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/63996281.html
匿名

发表评论

匿名网友

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

确定