自定义注解的场景及实现

自定义注解使用场景

  • 类属性自动赋值
  • 验证对象属性完整性
  • 代替配置文件功能(例如spring基于注解的配置)
  • 可以生成文档(例如javadoc的@see@param)

    自定义注解的实现

    注解是插入到源码中使用其他工具可以对其进行处理的标签

    下面是一个简单注解的实例

    1
    2
    3
    4
    public class MyClass{
    ...
    @Test pulbic void checkRandomInsertions()
    }

  在java中,注解是当作一个修饰符来使用的,它被置于注解项之前,中间没有分号;注解是代码的一部分
  @Test 注解本身并不会做任何事情,它需要工具支持才会有用。例如,当测试一个类的时候,JUnit4 测试工具可能会调用所有标识为@Test 的方法。另一个工具可能会删除一个类文件的所有测试方法,以便在对这个类测试完毕后,不会将这些测试方法和程序装载在一起。
  注解可以定义成包含元素的形式,例如:

1
@Test(timeout= "10000")

每个注解都必须通过一个注解接口进行定义。这些接口中的方法与注解中的元素相对应。例如,Junit 的注解Test可以用下面这个接口进行定义:

1
2
3
4
5
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test{
long timeout() default OL;
}

@interface声明创建了一个真正的java接口。处理注解的工具将接收那些实现了这个注解接口的对象。这类工具可以调用timeout方法来检索某个特定Test注解的timeout元素。
注解Target和Retention是元注解。他们注解了Test注解,即将Test注解标识成一个只能运用到方法上的注解,并且当类文件载入到虚拟机的时候,依然可以保留下来。

注解语法

注解接口

注解是由注解接口来定义的

1
2
3
4
modifiers @interface AnnotationName{
elementDeclaration1;
elementDeclaration2;
}

每个元素声明都具有下面这种形式:

1
type elementName();

或者

1
type elementName() default value;

注解元素的类型为下列之一:

  • 基本类型(int、short、long、byte、char、double、float或者boolean)
  • String
  • Class(具有一个可选的类型参数,例如Class<? extends MyClass>)
  • enum类型
  • 注解类型
  • 由前面所述类型组成的数组(由数组组成的数组不是合法的元素类型)
    下面是一个合法的元素声明的例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public @interface BugReport{
    enum Status { UNCONFIRMED, CONFIRMED, FIXED, NOTABUG};
    boolean showStopper() default false;
    String assignedTo() default "[none]";
    Class<?> testCase() default Void.class;
    Status status() default Status.UNCONFIRMED;
    Reference ref() default @Reference;//an annotation type
    String[] reprotedBy();
    }
注解

每个注解都具有下面这种格式:

1
@AnnotationName(elementName1=value1, elementName2=value2, ...)

例如,

1
@BugReport(assignedTo="Harry", severity=10)

元素的顺序无关紧要。下面这个注解和前面那个一样

1
@BugReport(severity=10, assignedTo="Harry")

如果某个元素的值并未指定,那么就使用声明的默认值。例如,考虑一下下面这个注解

1
@BugReport(severity=10)

元素assignedTo的值是字符串”[none]”
有两个特殊的快捷方式可以用来简化注解。
如果没有指定元素,要么是因为注解中没有任何元素,要么是因为所有的元素都使用默认值,那么你就不需要用圆括号了。例如,

1
@BugReport

这样的注解也称为标记注解;
另一种快捷方式是单值注解。如果一个元素具有特殊的名字value,并且没有指定其他元素,那么你就可以忽略掉这个元素名以及等号。例如:

1
2
3
public @interface ActionListenerFor{
String value();
}

可以将注解书写成如下格式:

1
@ActionListenerFor("yellow")

而不是

1
@ActionListenerFor(value="yellow")

注解各类声明

注解可以出现在许多地方,这些地方可以分为两类:声明和类型用法。声明注解可以出现在下列声明出:

  • 类(包括enum)
  • 接口(包括注解接口)
  • 方法
  • 构造器
  • 实例域(包含enum常量)
  • 局部变量
  • 参数变量
  • 类型参数
    注解类型用法
    声明注解提供了正在被声明的项的相关信息。例如,在下面的声明中
    1
    public User getUser(@NonNull String userId)

就断言userId参数不为空
类型用法注解可以出现在下面的位置:

  • 与泛化类型引元一起使用
  • 数组中的任何位置
  • 与超类和实现接口一起使用
  • 与构造器调用一起使用:

    1
    new @Localized String(...)
  • 与强制转型和instanceof检查一起使用

    1
    (@Localized String)text, if(text instanceof @Localized String)
  • 与异常规约一起使用

  • 与通配符和类型边界一起使用
  • 与方法和构造器引用一起使用
    注解this
    了解一下
    1
    2
    3
    public class Point{
    public boolean equals(@ReadOnly Point this, @ReadOnly Object other){...}
    }

第一个参数被称为接收器参数,它必须被命名为this,而它的类型就是要构建的类。

标准注解

注解接口 应用场合 目的
Deprecated 全部 将项标记为过时
SuppressWarnings 除了包和注解之外的所有情况 阻止某个给定类型的警告信息
SafeVarargs 方法和构造器 断言varargs参数可安全使用
Override 方法 检查该方法是否覆盖了某一个超类方法
FunctionalInterface 接口 将接口标记为只有一个抽象方法的函数式接口
PostConstruct 方法 被标记的方法应该在构造之后立即被调用
PreDestroy 方法 被标记的方法应该在移除之前立即被调用
Resource 类、接口、方法、域 在类或接口上:标记为在其他地方要用到的资源。在方法或域上:为“注入”而标记
Resources 类、接口 一个资源组
Generated 全部
Target 注解 指明可以应用这个注解的那些项
Retention 注解 指明这个注解可以保留多久
Documented 注解 指明这个注解应该包含在注解项的文档中
Inherited 注解 指明当这个注解应用与一个类的时候,能够自动被它的子类继承
Repeatable 注解 指明这个注解可以在同一个项上应用多次
用于编译的注解
用于管理资源的注解
元注解

源码级注解处理

注解处理
1
javac -process ProcessorClassName1, ProcessorClassName2, ...sourceFiles
语言模型API
使用注解来生成源码

to be continued…