Java1.5引入了注解,当前许多java框架中大量使用注解,如Hibernate、Jersey、Spring。
注解作为程序的元数据嵌入到程序当中。注解可以被一些解析工具或者是编译工具进行解
析。我们也可以声明注解在编译过程或执行时产生作用。它还可以作用于程序运行过程中、
注解解释器可以通过注解决定程序的执行顺序。
JDK 基本Annotation
注解 | 说明 |
---|---|
@Override | 重写 |
@Deprecated | 已过时 |
@SuppressWarnings(value = “unchecked”) | 压制编辑器警告 |
@SafeVarargs | 修饰”堆污染”警告 |
@FunctionalInterface | Java8特有的函数式接口 |
JDK 元Annotation
元Annotation用于修饰其他的Annotation定义.
元注解 | 释义 |
---|---|
@Retention | 注解保留策略 |
@Target | 注解修饰目标 |
@Documented | 注解文档提取 |
@Inherited | 注解继承声明 |
1. Documented
当一个注解类型被@Documented元注解所描述时,那么无论在哪里使用这个注解,都会被
Javadoc工具文档化。我们来看一下它的定义:
2. Inherited
表明被修饰的注解类型是自动继承的。具体解释如下:若一个注解类型被Inherited元注
解所修饰,则当用户在一个类声明中查询该注解类型时,若发现这个类声明中不包含这个
注解类型,则会自动在这个类的父类中查询相应的注解类型,这个过程会被重复,直到该
注解类型被找到或是查找完了Object类还未找到。
3. Retention
它表示一个注解类型会被保留到什么时候,Java Annotation对应的Retention有3种,在RetentionPolicy中定义,有3种:
SOURCE: 注解保留在源代码中,但是编译的时候会被编译器所丢弃。比如@Override, @SuppressWarnings、logbok的注解
CLASS.:这是默认的policy。注解会被保留在class文件中,但是在运行时期间就不会识别这个注解。
RUNTIME: 注解会被保留在class文件中,同时运行时期间也会被识别。所以可以使用反射机制获取注解信息。比如@Deprecated
4. Target
这个元注解说明了被修饰的注解的应用范围,也就是被修饰的注解可以用来注解哪些程序元素,它的定义如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
从以上定义我们可以看到它也会保留到运行时,而且它的取值是为ElementType[]类型(一个数组,意思是可以指定多个值),ElementType是一个枚举类型,它可以取以下值:
TYPE:表示可以用来注解类、接口、注解类型或枚举类型;
PACKAGE:可以用来注解包;
PARAMETER:可以用来注解参数;
ANNOTATION_TYPE:可以用来注解 注解类型;
METHOD:可以用来注解方法;
FIELD:可以用来注解属性(包括枚举常量);
CONSTRUCTOR:可以用来注解构造器;
LOCAL_VARIABLE:可用来注解局部变量。
自定义注解
我们可以创建我们自己的注解类型并使用它。请看下面的示例:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface MethodInfo {
String author() default "absfree";
String date();
int version() default 1;
}
在自定义注解时,有以下几点需要我们了解:
注解类型是通过”@interface“关键字定义的;
在”注解体“中,所有的方法均没有方法体且只允许public和abstract这两种修饰符号(不加修饰符缺省为public),注解方法不允许有throws子句;
注解方法的返回值只能为以下几种:原始数据类型), String, Class, 枚举类型, 注解和它们的一维数组,可以为方法指定默认返回值。
Java注解解析
我们将使用反射技术来解析java类的注解。那么注解的RetentionPolicy应该设置为RUNTIME
否则java类的注解信息在执行过程中将不可用那么我们也不能从中得到任何和注解有关的数据。
Class clas = Class.forName("com.github.chengbin.auth.ListTest");
for (Method method : clas.getMethods()) {
RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class);
if(rateLimiter != null){
System.out.println("注解RateLimiter");
}
}
使用Annotation修饰了类/方法/成员变量等之后,这些Annotation不会自己生效,必须由这些注解的开发者提供相应的工具来提取并处理Annotation信息(当然,只有当定义Annotation时使用了@Retention(RetentionPolicy.RUNTIME)修饰,JVM才会在装载class文件时提取保存在class文件中的Annotation,该Annotation才会在运行时可见,这样我们才能够解析).
Java使用Annotation接口来代表程序元素前面的注解, 用AnnotatedElement接口代表程序中可以接受注解的程序元素.像Class Constructor FieldMethod Package这些类都实现了AnnotatedElement接口.
注解添加监听器
下面通过使用Annotation简化事件编程, 在传统的代码中总是需要通过addActionListener方法来为事件源绑定事件监听器,这里使用注解绑定事件监听器:
自定义注解TimeAdvice:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimeAdvice{
Class<? extends TimeAdviceListener> listener();
}
注解监听器TimeAdviceListener:
public class TimeAdviceListener {
public TimeAdviceListener() {
init();
}
public void init(){
System.out.println("注解监听器进来了..");
}
public void handler(){
//TODO 处理逻辑
}
}
方法添加注解:
public class UserDao implements IUserDao {
private static final Map<Long, User> users = new ConcurrentHashMap<>();
@Override
@TimeAdvice(listener = TimeAdviceListener.class)
public void insert(User user) {
Long id = IdWorker.INSTANCE.nextId();
user.setId(id);
users.put(id,user);
}
@Override
public void delete(Long id) {
User user = select(id);
users.remove(user);
}
@Override
public void update(User user) {
users.put(user.getId(),user);
}
@Override
public User select(Long id) {
return users.get(id);
}
}
参考:
1.http://www.importnew.com/17524.html
2.http://www.importnew.com/23816.html