【问题标题】:How to create an instance of an annotation如何创建注解的实例
【发布时间】:2013-04-24 08:42:45
【问题描述】:

我正在尝试做一些 Java 注释魔术。我必须说我仍在追赶注释技巧,并且某些事情对我来说还不是很清楚。

所以...我有一些带注释的类、方法和字段。我有一个方法,它使用反射对类运行一些检查并将一些值注入到一个类中。这一切都很好。

但是,我现在面临一个需要注释实例(可以这么说)的情况。所以......注释不像常规接口,你不能对一个类进行匿名实现。我知道了。我在这里查看了一些关于类似问题的帖子,但我似乎无法找到我正在寻找的答案。

我基本上想获取注释的实例,并能够使用反射设置其中的一些字段(我想)。有没有办法做到这一点?

【问题讨论】:

  • 您不能实例化或修改注释。当您运行代码时,它们已经存在。您只能检索它们。您能否提供一些代码示例来说明您正在尝试做的事情?
  • 你试过java.lang.Class/java.lang.reflect.Method中的getAnnotation()
  • 有趣的问题。我试着环顾四周,得到了这些。 stackoverflow.com/questions/266903/… 看看拉尔夫的第一个答案。我认为应该这样做。 stackoverflow.com/questions/2786292/…这会有帮助吗?
  • 你试过getClass().getAnnotations()吗?
  • 我不是要检查一个类是否用某个注释进行了注释。这部分注入我已经使用反射实现了。我有一个极端情况,我必须将 MyAnnotation 添加到 Set<MyAnnotation> 并且我没有注释的实例。

标签: java reflection annotations


【解决方案1】:

看看AnnoBuilder。好处是它可以使用方法引用而不是属性名

@interface Foo
{
    String value();
    int[] flags() default {0};
}

//test

    // @Foo(value="abc", flags={1})
    Foo foo1 = AnnoBuilder.of(Foo.class)
        .def(Foo::value, "abc")
        .def(Foo::flags, 1)
        .build();

    // @Foo(value="abc")
    Foo foo2 = AnnoBuilder.build(Foo.class, Foo::value, "abc");

    // @Foo("abc")
    Foo foo3 = AnnoBuilder.build(Foo.class, "abc");

【讨论】:

    【解决方案2】:

    Gunnar's answer 中建议的代理方法已在 GeantyRef 中实现:

    Map<String, Object> annotationParameters = new HashMap<>();
    annotationParameters.put("name", "someName");
    MyAnnotation myAnnotation = TypeFactory.annotation(MyAnnotation.class, annotationParameters);
    

    这将产生一个等同于您从中获得的注释:

    @MyAnnotation(name = "someName")
    

    以这种方式生成的注解实例将与 Java 正常生成的实例相同,并且它们的 hashCodeequals 已正确实现以实现兼容性,因此没有像在接受的答案中直接实例化注解这样的奇怪警告.事实上,JDK 内部也使用了同样的方法:sun.reflect.annotation.AnnotationParser#annotationForMap

    库本身很小,没有依赖项(并且不依赖于 JDK 内部 API)。

    披露:我是 GeantyRef 背后的开发者。

    【讨论】:

    • 我更喜欢这种方法而不是其他方法,因为它提供了最好的结果(与 Gunnar's Answer 相同),并且因为它不使用 Oracle JDK 内部类,如 Tobias 的答案,这可能会使它与其他 JDK/JVM 不兼容。
    • 很遗憾,您在此处抛出的异常是已检查异常,它仅用于指示给定参数的问题——通常应该是运行时异常。
    • @john16384 这是为了尽可能地模拟 Java 自己的 API,无论好坏。但事后看来,这可能不是最好的主意,因为这种方法服务于与 Java 内部注释工厂截然不同的场景。我可能会在下一个版本中更改它。
    【解决方案3】:

    @Gunnar 的回答是大多数网络服务最简单的方法,因为我们已经休眠了, 例如 KafkaListener kafkaListener = new org.hibernate.validator.internal.util.annotation.AnnotationDescriptor.Builder&lt;&gt;(KafkaListener.class, ImmutableMap.of("topics", new String[]{"my-topic"})).build().getAnnotation(); 和所有其他属性将保持默认。

    【讨论】:

      【解决方案4】:

      您也可以绝对愚蠢地(但简单地)创建一个虚拟注释目标并从那里获取它

      @MyAnnotation(foo="bar", baz=Blah.class)
      private static class Dummy {}
      

      final MyAnnotation annotation = Dummy.class.getAnnotation(MyAnnotation.class)
      

      创建以方法/参数为目标的注解实例可能会稍微复杂一些,但这种方法的好处是可以像 JVM 通常那样获取注解实例。 不用说它是尽可能简单的。

      【讨论】:

      • 这是最优雅的解决方案。
      【解决方案5】:

      我这样做是为了在我的焊接单元测试中添加注释参考:

      @Qualifier
      @Retention(RetentionPolicy.RUNTIME)
      @Target({ METHOD, FIELD, PARAMETER })
      public @interface AuthenticatedUser {
      
          String value() default "foo";
      
          @SuppressWarnings("all")
          static class Literal extends AnnotationLiteral<AuthenticatedUser> implements AuthenticatedUser {
      
              private static final long serialVersionUID = 1L;
      
              public static final AuthenticatedUser INSTANCE = new Literal();
      
              private Literal() {
              }
      
              @Override
              public String value() {
                  return "foo";
              }
          }
      }
      

      用法:

      Bean<?> createUserInfo() {
          return MockBean.builder()
                  .types(UserInfo.class)
                  .qualifiers(AuthenticatedUser.Literal.INSTANCE)
                  .create((o) -> new UserInfo())
                  .build();
      }
      

      【讨论】:

        【解决方案6】:

        您可以使用 Hibernate Validator 项目中的注解代理,例如 this one(免责声明:我是该项目的提交者)。

        【讨论】:

        • 那些链接现在已经坏了,你有没有机会内联应该存在的代码?
        • 更新了一个链接,删除了另一个。
        【解决方案7】:

        在 Apache Commons AnnotationUtils 的帮助下使用代理方法相当粗糙

        public static <A extends Annotation> A mockAnnotation(Class<A> annotationClass, Map<String, Object> properties) {
            return (A) Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class<?>[] { annotationClass }, (proxy, method, args) -> {
                Annotation annotation = (Annotation) proxy;
                String methodName = method.getName();
        
                switch (methodName) {
                    case "toString":
                        return AnnotationUtils.toString(annotation);
                    case "hashCode":
                        return AnnotationUtils.hashCode(annotation);
                    case "equals":
                        return AnnotationUtils.equals(annotation, (Annotation) args[0]);
                    case "annotationType":
                        return annotationClass;
                    default:
                        if (!properties.containsKey(methodName)) {
                            throw new NoSuchMethodException(String.format("Missing value for mocked annotation property '%s'. Pass the correct value in the 'properties' parameter", methodName));
                        }
                        return properties.get(methodName);
                }
            });
        }
        

        传递的属性类型不与注解接口上声明的实际类型进行检查,任何缺失值仅在运行时发现。

        在功能上与kaqqao's answer 中提到的代码非常相似(可能还有Gunnar's Answer),但没有像Tobias Liefke's answer. 那样使用内部Java API 的缺点

        【讨论】:

          【解决方案8】:

          你可以使用sun.reflect.annotation.AnnotationParser.annotationForMap(Class, Map):

          public @interface MyAnnotation {
              String foo();
          }
          
          public class MyApp {
              public MyAnnotation getInstanceOfAnnotation(final String foo) {
                  MyAnnotation annotation = AnnotationParser.annotationForMap(
                      MyAnnotation.class, Collections.singletonMap("foo", "myFooResult"));
              }
          }
          

          缺点:sun.* 中的类在以后的版本中可能会发生变化(尽管这种方法从 Java 5 开始就存在,具有相同的签名)并且不适用于所有 Java 实现,请参阅this discussion

          如果这是一个问题:您可以使用自己的 InvocationHandler 创建一个通用代理 - 这正是 AnnotationParser 在内部为您所做的。或者您使用您自己的MyAnnotation 实现,定义为here。在这两种情况下,您都应该记住实现annotationType()equals()hashCode(),因为结果是专门为java.lang.Annotation 记录的。

          【讨论】:

          • 最佳答案 IMO
          【解决方案9】:

          嗯,显然没有那么复杂。 真的!

          正如一位同事所指出的,您可以像这样简单地创建注释的匿名实例(就像任何接口一样):

          我的注释:

          public @interface MyAnnotation
          {
          
              String foo();
          
          }
          

          调用代码:

          class MyApp
          {
              MyAnnotation getInstanceOfAnnotation(final String foo)
              {
                  MyAnnotation annotation = new MyAnnotation()
                  {
                      @Override
                      public String foo()
                      {
                          return foo;
                      }
          
                      @Override
                      public Class<? extends Annotation> annotationType()
                      {
                          return MyAnnotation.class;
                      }
                  };
          
                  return annotation;
              }
          }
          

          感谢Martin Grigorov

          【讨论】:

          • 嗯,看来你还需要实现annotationType。大概这只会返回MyAnnotation.class
          • 有趣的事情要注意。我实际上已经让 IDE 生成方法,但没有注意到它也实现了该方法。它被留下返回null 并工作。我已将其设置为注释的类类型,虽然我不确定它应该是什么。
          • 警告:MyAnnotation 的匿名实例是类的实例,而不是注释,即 annotation.getClass().isInterface()annotation.getClass().isAnnotation() 都返回 false
          • 另一个警告:annotation.getClass() 也没有MyAnnotation 的潜在元注释。要访问它们,必须先获得原始类:annotation.getClass().getInterfaces()[0]。当我需要在测试中模拟注释时遇到了这个问题。 (注意@Inherited 无济于事,因为它是关于继承具有注释的类,而不是注释本身。)
          • 根据Annotation的JavaDoc,您还需要实现equalshashCode。例如,java.inject.Inject 的限定符注释可能无法找到正确的 bean。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-03-16
          • 1970-01-01
          • 1970-01-01
          • 2016-03-20
          • 1970-01-01
          • 2011-01-06
          • 1970-01-01
          相关资源
          最近更新 更多