如何创建注释的实例

时间:2013-04-30 12:17:24

标签: java reflection annotations

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

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

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

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

8 个答案:

答案 0 :(得分:61)

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

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

MyAnnotation:

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的信用。

答案 1 :(得分:10)

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

库本身很小,没有依赖性。

披露:我是GeAnTyRef背后的开发人员。

答案 2 :(得分:7)

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

答案 3 :(得分:5)

您可以使用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进行记录。

答案 4 :(得分:1)

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

@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();
}

答案 5 :(得分:1)

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

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

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

创建以方法/参数为目标的注释实例可能会更加复杂,但是这种方法的好处是可以像JVM通常那样获得注释实例。 不用说,它尽可能地简单。

答案 6 :(得分:0)

借助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的缺点。

答案 7 :(得分:0)

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