Annotation总结

从JKD5开始,java增加了对元数据的支持,也就是Annotation,这是代码里的特殊标记,这些标记可以在编译、类加载、运行的是时候被读取,并且能够执行相应的处理。
通过使用Annotation ,我们可以在开发过程中,在不改变原有逻辑的情况下,在源文件中补充一些信息。
Annotation 可以用于修饰包、类、构造器、方法、成员变量、参数和局部变量的声明。

四个基本的Annotation

@Override (重写父类方法)

用来指定方法覆载的,它可以强制一个子类必须覆盖父类的方法。
在使用这个注解的时候,可能看不出来效果,这个的主要作用是检查一下父类中,是不是有一个相同的方法被重写!

@Deprecated(标示已经过时)

用于表示某个程序元素已经过时,当其他程序使用已经过时的类、方法的时候,编译器将会给出警告。

@SuppressWarnings(抑制编译器警告)

指示被该Annotation修饰的程序元素取消显示指定的编译器警告,会一直作用于该程序元素的所有子元素。在使用的过程总,一定要在括号里面使用name = value的形式 !即 @SuppressWarnings(value = “unchecked”)
unchecked就是一个未经过检查的转换

java7 的堆污染警告@SafeVarargs

当把一个不带范型的对象赋给一个带范型的变量的时候,往往就会发生“堆污染”。例如:

1
2
List list = new ArrayList<Integer>();
list.add(20);

对于形式参数可变的方法,该形参类型又是范型,这将更容易导致“堆污染”。
(java不支持范型数组)

JDK基本的元Annotation

@Retention

只能用来修饰一个Annotation定义,用于指定被修饰的Annotation可以存活多长时间。后面必须跟着一个值(三个限定)
例如:

1
2
@Retention(value=RetentionPolicy.SOURCE)
public @interface Testtable{}

@Target

只能修饰一个Annotation定义,用于指定被修饰的Annotation能用于修饰哪些程序单元。(有八个程序变量的值)
例如:

1
2
@Target(ElementType.FIELD)
public @interface ActionListenerErro{}

@Documented

用于指定这个Annotation类可以被javadoc工具提取成文档.

1
2
3
4
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
//使用Documented修饰 一个类 用作Annotation ,这个类可以被javadoc工具提取成文档
public @interface Testtable{}

然后使用Testtable修饰 其他类的方法,就会被提取成文档

1
2
3
4
5
6
7
8
public class MyTest
{
@Testtable
public void info()
{
System.out.println("info方法...");
}
}

@Inherited

如果定义某一个Annotation的时候,这个类被@Inherited修饰,则继承了该类的子类将会被@A修饰。

1
2
3
4
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Inheritable{}

上面程序中粗体字代码表明@Inheritable 具有继承性,如果某个类使用了@Inheritable修饰,则该类的子类将自动使用@Inheritable修饰

1
2
3
4
5
6
7
8
9
10
11
12
@Inheritable
class Base[
}
//InheritableTest 类只继承了Base类
// 并未直接使用@Inheritable Annotiation修饰
public class InheritableTest extends Base[
public static void main(String [] args)
{
//打印InheritableTest类是否具有@Inheritable修饰
System.out.println(InheritableTest.class.isAnnotationPresent(Inheritable.class));
}
}

上面程序中的Ba se类使用@Inheritable修饰,而该Annotation具有继承性,所以其子类也将自动使用@Inheritable修饰。上面程序输出 true

自定义Annotation

自定义Annotation使用@interface关键字定义一个新的Annotation 类型与定义一个接口非常像。

标记Annotation(无参数)

1
2
3
//定义一个简单的Annotation
public @interface Test{
}

使用Annotation的时候,通常可以用来修饰程序中的类、方法、变量、接口等定义。通常Annotation放在所有的修饰符之前。

元数据Annotation(有参数)

1
2
3
4
public @interface mytage{
String name();
int age();
}

带有成员变量的Annotation 的定义是以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。
在使用的时候,应该写为

1
@mytage(name="xx",age=6)

也可以设置默认值

1
2
3
4
public @interface mytage{
String name() default "yeeku";
int age() default 2;
}

提取Annotation信息

开发者使用Annotation 修饰了类、方法、Field等成员之后,这些Annotation不会自己生效,必须由开发者提供相应的工具来提取并处理Annotation信息。

java5在java.lang.reflect包下新增加了AnnotatedElement接口,该接口代表程序中可以接受注释的程序元素。
该接口主要有如下几个实现类:

  • Class 类定义
  • Constructor 构造器定义
  • Field 类的成员变量定义
  • Method 类的方法定义
  • Package 类的包定义

程序可以通过调用某一个类的如下三种方法来访问Annotation信息

  • getAnnotation(Class annoClass)
  • Annotation[] getAnnotations() 返回该程序元素上存在的所有的注释
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass): 判断该程序上是否存在指定类型的注释,如果存在则返回true,否则返回false.
    例如:获取Test类的info 方法里面的所有的注释
1
Annotation [] aArray = Class.forName("Test").getMethod("info").getAnnotations()

使用Annotation 的示例

仅仅使用注释来标记程序元素是不会有任何影响的,为了让程序中的这些注释起作用,接下来必须为这些注释提供一个注释处理工具,注释处理工具通过分析目标类中的注释,然后在通过反射来运行标记的测试方法,程序可以做出相应的处理。

示例

在示例程序中通过使用@ActionListenerFor来为程序中的按钮绑定事件监听器

  • 首先创建一个标记,该标记需要传入一个监听器实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package AnnotationExample;

import java.awt.event.ActionListener;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionListenerFor {
//定义一个成员变量,用于设置元数据
//该listener 成员变量用于保存监听器实现类
Class<? extends ActionListener> listener();
}
  • 然后,创建两个监听器的实现类
    确认按钮实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package AnnotationExample;

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    import javax.swing.JOptionPane;

    public class OkListener implements ActionListener{

    @Override
    public void actionPerformed(ActionEvent e) {
    // TODO Auto-generated method stub
    JOptionPane.showMessageDialog(null,"单击了确认按钮");
    }

    }
  • 取消按钮实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package AnnotationExample;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JOptionPane;

public class CancelListener implements ActionListener{

@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JOptionPane.showMessageDialog(null,"单击了取消按钮");
}
}
  • 如果仅仅在程序中使用注释是不会起任何作用的,需要编写一个注释处理工具来处理程序中的注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package AnnotationExample;

import java.awt.event.ActionListener;
import java.lang.reflect.Field;

import javax.swing.AbstractButton;

public class ActionListenerInstaller {
//处理Annotation 的方法,其中obj是包含Annotation的对象
public static void processAnnotations(Object obj)
{
try {
Class cl = obj.getClass();
//获取指定的obj 对象的所有Field 并遍历每个field
for(Field f: cl.getDeclaredFields()) {
//将指定的Field设置成可自由访问
f.setAccessible(true);
//获取指定Field 上的ActionListenerFor类型的Annotation
ActionListenerFor a = f.getAnnotation(ActionListenerFor.class);
//获取f Field 实际对应的对象
Object fobj = f.get(obj);
//如果f是AbstractButton 的实例,并且a不为null
if(a!=null && fobj != null && fobj instanceof AbstractButton) {
//获取a注释 里的元数据listener (它是一个监听器类)
Class<? extends ActionListener> listenerClazz = a.listener();
//使用反射来创建listener类的对象
ActionListener al = listenerClazz.newInstance();
AbstractButton ab = (AbstractButton)fobj;
//为ab按钮添加事件监听器
ab.addActionListener(al);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
  • 主程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package AnnotationExample;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AnnotationTest {
private JFrame mainWin = new JFrame("使用注释绑定事件监听器");
//使用Annotation 为ok 按钮绑定事件监听器
@ActionListenerFor(listener = OkListener.class)
private JButton ok = new JButton("确定");
@ActionListenerFor(listener = CancelListener.class)
private JButton cancle = new JButton("取消");
public void init()
{
//初始化界面的方法
JPanel jp = new JPanel();
jp.add(ok);
jp.add(cancle);
mainWin.add(jp);
//
ActionListenerInstaller.processAnnotations(this);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}
public static void main(String[] args) {
new AnnotationTest().init();
}
}

编译时处理Annotation

APT(Annotation Processing Tool)是一种注释处理工具,它对源代码文件进行检测找出其中的Annotation后,对Annotation 进行额外的处理。
Annotation处理器 在处理Annotation的时候可以根据源文件中的Annotation生成额外的源文件和其他文件,APT还会编译生成的源代码文件和原来的源文件,将他们一起生成class文件。