我正在尝试做一些Java注释魔术。我必须说我仍在追赶注释技巧,而且某些事情对我来说仍然不太清楚。
所以...我有一些带注释的类,方法和字段。我有一个方法,它使用反射对类运行一些检查并将一些值注入到类中。一切正常。
但是,我现在面临的情况是我需要一个注释实例(可以这么说)。所以...注释不像常规接口,你不能做一个类的匿名实现。我知道了。我在这里看了一些关于类似问题的帖子,但我似乎无法找到我想要的答案。
我基本上想得到一个注释的实例,并且能够使用反射设置它的一些字段(我想)。有没有办法做到这一点?
答案 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生成的注释实例相同,并且它们的hashCode
和equals
已经正确实现以实现兼容性,因此没有像直接实例化注释那样奇怪的注意事项在接受的答案中。事实上,JDK内部使用相同的方法:sun.reflect.annotation.AnnotationParser#annotationForMap。
库本身很小,没有依赖性。
披露:我是GeAnTyRef背后的开发人员。
答案 2 :(得分:7)
答案 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();
和所有其他属性将保持默认。