Java 如何创建注解的实例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16299717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:13:12  来源:igfitidea点击:

How to create an instance of an annotation

javareflectionannotations

提问by carlspring

I am trying to do some Java annotation magic. I must say I am still catching up on annotation tricks and that certain things are still not quite clear to me.

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

So... I have some annotated classes, methods and fields. I have a method, which uses reflection to run some checks on the classes and inject some values into a class. This all works fine.

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

However, I am now facing a case where I need an instance (so to say) of an annotation. So... annotations aren't like regular interfaces and you can't do an anonymous implementation of a class. I get it. I have looked around some posts here regarding similar problems, but I can't seem to be able to find the answer to what I am looking for.

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

I would basically like to get and instance of an annotation and be able to set some of it's fields using reflection (I suppose). Is there at all a way to do this?

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

采纳答案by carlspring

Well, it's apparently nothing all that complicated. Really!

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

As pointed out by a colleague, you can simply create an anonymous instance of the annotation (like any interface) like this:

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

MyAnnotation:

我的注解:

public @interface MyAnnotation
{

    String foo();

}

Invoking code:

调用代码:

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;
    }
}

Credits to Martin Grigorov.

归功于Martin Grigorov

回答by Gunnar

You could use an annotation proxy such as this onefrom the Hibernate Validator project (disclaimer: I'm a committer of this project).

您可以使用一个注解代理,例如来自 Hibernate Validator 项目的这个代理(免责声明:我是这个项目的提交者)。

回答by Tobias Liefke

You can use sun.reflect.annotation.AnnotationParser.annotationForMap(Class, Map):

您可以使用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"));
    }
}

Downside: Classes from sun.*are subject to change in later versions (allthough this method exists since Java 5 with the same signature) and are not available for all Java implementations, see this discussion.

缺点:来自sun.*更高版本的类可能会发生变化(尽管自 Java 5 以来就存在具有相同签名的此方法)并且不适用于所有 Java 实现,请参阅此讨论

If that is a problem: you could create a generic proxy with your own InvocationHandler- this is exactly what AnnotationParseris doing for you internally. Or you use your own implementation of MyAnnotationas defined here. In both cases you should remember to implement annotationType(), equals()and hashCode()as the result is documented specifically for java.lang.Annotation.

如果这是一个问题:您可以使用自己的代理创建一个通用代理InvocationHandler- 这正是AnnotationParser您在内部所做的事情。您也可以使用自己实现MyAnnotation的定义在这里。在这两种情况下,你应该记住落实annotationType()equals()hashCode()作为结果是具体文件java.lang.Annotation

回答by kaqqao

The proxy approach, as suggested in Gunnar's answeris already implemented in GeAnTyRef:

正如Gunnar 的回答中所建议的那样,代理方法已经在GeAnTyRef 中实现了:

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

This will produce an annotation equivalent to what you'd get from:

这将产生一个注释,相当于你从中得到的:

@MyAnnotation(name = "someName")

Annotation instances produced this way will act identical to the ones produced by Java normally, and their hashCodeand equalshave been implemented properly for compatibility, so no bizarre caveats like with directly instantiating the annotation as in the accepted answer. In fact, JDK internally uses this same approach: sun.reflect.annotation.AnnotationParser#annotationForMap.

注释实例这种方式生产将起到相同的Java的正常产生的,他们hashCodeequals已经正确实施的兼容性,所以没有离奇的注意事项就像在实例化注释作为公认的答案。事实上,JDK 内部使用了同样的方法:sun.reflect.annotation.AnnotationParser#annotationForMap

The library itself is tiny and has no dependencies.

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

Disclosure:I'm the developer behind GeAnTyRef.

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

回答by oujesky

Rather crude way using the proxy approach with the help of Apache Commons AnnotationUtils

在 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);
        }
    });
}

The types of passed properties are not checked with the actual type declared on the annotation interface and any missing values are discovered only during runtime.

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

Pretty similar in function to the code mentioned in kaqqao's answer(and probably Gunnar's Answeras well), without the downsides of using internal Java API as in Tobias Liefke's answer.

在功能上与kaqqao 的答案(也可能是Gunnar 的答案)中提到的代码非常相似,没有像Tobias Liefke 的答案那样使用内部 Java API 的缺点

回答by Fabrizio Stellato

I did this for adding annotation reference on my weld unit test:

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

@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";
        }
    }
}

usage:

用法:

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

回答by Ravi Sanwal

You can also absolutely stupidly (but simply) create a dummy annotation target and get it from there

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

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

And

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

Creating method/parameter targeted annotation instances may be a little more elaborate, but this approach has the benefit of getting the annotation instance as the JVM would normally do. Needless to say it is as simple as it can get.

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