实现注释的有效用例是什么?
在设计主要基于注释的配置系统时,我偶尔需要创建实现代码生成或编程配置注释的类。
备选方案涉及将注释中包含的数据镜像到DTO中,这似乎是一种开销。
以下是一个例子:
public enum IDType {
LOCAL,
URI,
RESOURCE;
}
@Documented
@Target( { METHOD, FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Id {
/**
* @return
*/
IDType value() default IDType.LOCAL;
}
实施
public class IdImpl implements Id{
private final IDType idType;
public IdImpl(IDType idType){
this.idType = idType;
}
@Override
public IDType value() {
return idType;
}
@Override
public Class<? extends Annotation> annotationType() {
return Id.class;
}
}
我收到编译器警告,但它似乎是许多用例的有效工具。
上述示例的警告是
注释类型ID不应该是 用作IdImpl
的超接口
编辑:
我刚从Guice找到了这个例子:
bind(CreditCardProcessor.class)
.annotatedWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);
请参阅此Javadoc from Names。
有没有人知道为什么存在此限制或者还有其他一些用例?
答案 0 :(得分:20)
我从未在实践中使用它,但你得到的是,你可以使用类来代替你的注释。
让我们创造一个人为的例子。假设我们有一个文档生成器。它从给定的类中读取@Docu
注释并打印description
属性。像这样:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
public class DokuGenerator {
public static void main(String[] args) throws Exception {
new DokuGenerator(StaticClass.class, StaticClass2.class);
}
public DokuGenerator(Class<?>... classesToDokument) throws Exception {
List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument);
printDocumentation(documentAnnotations);
}
private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument)
throws Exception {
List<Docu> result = new ArrayList<Docu>();
for (Class<?> c : classesToDokument)
if (c.isAnnotationPresent(Docu.class))
result.add(c.getAnnotation(Docu.class));
return result;
}
private void printDocumentation(List<Docu> toDocument) {
for (Docu m : toDocument)
System.out.println(m.description());
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Docu {
String description();
}
@Docu(description = "This is a static class!")
class StaticClass {
}
@Docu(description = "This is another static class!")
class StaticClass2 {
}
打印:
This is a static class!
This is another static class!
我们现在想要实现的是,一个类不仅可以进行静态注释,还可以将运行时信息添加到文档中。我们很乐意在大多数时间使用@Docu
注释,但在特殊情况下我们需要特殊的文档。我们可能想为某些方法添加性能文档。我们可以通过让类实现注释来实现。生成器首先检查注释,如果不存在,则检查类是否实现注释。如果是,则将该类添加到注释列表中。
像这样(生成器中只有两行代码):
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DokuGenerator {
public static void main(String[] args) throws Exception {
new DokuGenerator(StaticClass.class, StaticClass2.class,
DynamicClass.class);
}
public DokuGenerator(Class<?>... classesToDokument) throws Exception {
List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument);
printDocumentation(documentAnnotations);
}
private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument)
throws Exception {
List<Docu> result = new ArrayList<Docu>();
for (Class<?> c : classesToDokument)
if (c.isAnnotationPresent(Docu.class))
result.add(c.getAnnotation(Docu.class));
else if (Arrays.asList(c.getInterfaces()).contains(Docu.class))
result.add((Docu) c.newInstance());
return result;
}
private void printDocumentation(List<Docu> toDocument) {
for (Docu m : toDocument)
System.out.println(m.description());
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Docu {
String description();
}
@Docu(description = "This is a static class!")
class StaticClass {
}
@Docu(description = "This is another static class!")
class StaticClass2 {
}
class DynamicClass implements Docu {
public DynamicClass() {
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
// ignore exception to make debugging a little harder
}
}
@Override
public String description() {
long millis = System.currentTimeMillis();
new DynamicClass();
millis = System.currentTimeMillis() - millis;
return "This is a dynamic class. I run on "
+ System.getProperty("os.name")
+ ". The construction of an instance of this class run for "
+ millis + " milliseconds.";
}
@Override
public Class<? extends Annotation> annotationType() {
return Docu.class;
}
}
输出是:
This is a static class!
This is another static class!
This is a dynamic class. I run on Windows XP. The construction of an instance of this class run for 47 milliseconds.
您不必更改代码生成器,因为您可以使用该类替换注释。
其他示例可以是使用注释或XML作为配置的框架。您可能有一个处理注释的处理器。如果您使用XML作为配置,您可以生成实现注释的类的实例,并且您的处理器可以对它们进行操作而无需进行任何更改! (当然还有其他方法可以达到同样的效果,但这是一种方法)
答案 1 :(得分:4)
JAXBIntroductions是一个很好的例子:它允许使用XML文件配置JAXB注释。我想到了两个主要用例:配置您没有源访问权限的类或一个类的不同配置。
通常,我认为动态实例化注释以将它们传递给框架通常是一个很好的用例。但是,如果你是这个框架的设计者,我当然会考虑三次。
答案 2 :(得分:0)
我在创建注释时使用它,并希望通过在省略注释时提供默认值来使其可选。当您的库引入新注释并且您希望库保持向后兼容时,可能会发生这种情况。
在这个例子中,BeanB是用源代码编写的,而不是你的库的旧版本,因此你想在识别这样一个类时使用默认值。
@Id
class BeanA {}
// No annotation
class BeanB {}
默认实施;
private static final Id DEFAULT_ID = new Id() {
@Override
public IDType value() {
return IDType.LOCAL;
}
@Override
public Class<? extends Annotation> annotationType() {
return Id.class;
}
};
处理;
Id beanId = (bean.getClass().isAnnotationPresent(Id.class))
? bean.getClass().getAnnotation(Id.class)
: DEFAULT_ID;
答案 3 :(得分:-2)
没有有效的用户案例 - 编译器只是对其进行处理,因为禁止它会非常混乱,编写编译器的人可能在非常罕见的情况下需要设施。如果您需要对注释进行分类,请查看本文以了解如何执行此操作:Why is not possible to extend annotations in Java?
Inagine一个可怜的灵魂来到你之后维护和调试那个代码或另一个需要编写codegen工具的人并假设annotatuion类型是直的或另一个只是使用这样的注释甚至没有梦想会发生什么和做什么关于它。当他发现黑客并找到消除它的方法时,他将死于疝气 - 或等效的疾病:-)注释应该是纯粹的声明性陈述,仅由与注释代码分开运行的codegen工具解释将其视为数据。
重新审视一下这段代码,然后小心翼翼地说出一个理性的rason,比如:
public Class<? extends Annotation> annotationType() {
return Id.class;
}
与人们可以放入代码相比,这是一件小事。
注释不是练习黑客的地方 - 这就是编译器试图传达的内容。您是否知道注释“实现”中的代码何时以及如何运行?包括CTOR?什么是可用的,什么不是在那一点?什么是安全的电话?编译器没有 - 编译器需要非常繁重的静态分析来检查这种黑客的实际安全性。所以它只是发出一个警告,以便当出现问题时,人们不能责怪编译,VM和其他一切。