1、介绍
从JDK5.0开始,Java增加了对元数据(MetaData)的支持,也就是Annotation。其实就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。
通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证
或者进行部署。
Annotation不影响程序代码的执行,无论增加、删除Annotation,代码都始终如一的执行。如果希望让程序中的Annotation能在运行时起一定的作用,只有通过某种配套的
工具对Annotation中的信息进行访问和处理,访问和处理Annotation的工具统称为APT(Annotation Processing Tool)。
2、原理
http://rejoy.iteye.com/blog/1627355
http://blog.csdn.net/blueheart20/article/details/18810693
可以参考JDK源码看其实现原理
3、使用
基本的Annotation
@Override:仅用来指定覆盖方法的,它强制一个子类必须要覆盖父类的方法。
@Deprecated:表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告。比如标记父类中的某个方法过时。
@SuppressWarnings("unchecked"):表示被Annotation标示的程序元素(以及在该程序元素中的所有子元素)取消显示指定的编译器警告。
自定义Annotation
使用关键字@interface
package com.java.anotion;
public @interface MyAnnotation {
//定义了两个变量的Annotation,default是默认值。
String name() default "cheney";
int age() default 18;
}
Annotation分为两类:
1、标记Annotation,没有成员定义,用自身的是否存在来为我们提供信息。
2、元数据Annotation,包含成员变量的Annotation,他们可以接受更多的元数据,所以被成为元数据Annotation。
提取Annotation的信息
java.lang.reflect新增的接口AnnotatedElement接口,该接口被主要的反射类实现,如Class,Constructor,Field,Method,Package等。通过反射类可以获取实现类的注释等信息。
两个示例:
示例一
package com.java.anotion;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
/**
* 空注释
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Testable {
}package com.java.anotion;
import java.lang.reflect.Method;
/**
*@Testable注释处理工具类
*/
public class TestProcessor {
/**
* 反射clazz调用的时候要加上报名称路径,不然会包找不到类异常
* @param clazz
* @throws SecurityException
* @throws ClassNotFoundException
*/
public static void process(String clazz) throws SecurityException, ClassNotFoundException{
int passed = 0;
int failed = 0;
for(Method m : Class.forName(clazz).getMethods()){
if(m.isAnnotationPresent(Testable.class)){
try {
m.invoke(null);
passed++;
} catch (Exception e) {
System.out.println("方法" + m + "运行失败,异常:" + e.getCause());
failed++;
}
}
}
System.out.println("共运行了" + (passed + failed) + "方法,其中成功的"
+ passed + "个,失败的" + failed + "个");
}
}package com.java.anotion;
/**
* 注释@Testable的目标类
*/
public class TestableAnnotationTest {
@Testable
public static void method1(){
}
public static void method2(){
}
@Testable
public static void method3(){
throw new RuntimeException("method3 RuntimeException");
}
public static void method4(){
}
@Testable
public static void method5(){
}
public static void method6(){
}
@Testable
public static void method7(){
throw new RuntimeException("method7 RuntimeException");
}
@Testable
public static void method8(){
}
}package com.java.anotion;
import java.util.ArrayList;
import java.util.List;
/**
* @author youyang
*
*
*/
public class Test extends Fruit{
@MyAnnotation(name="ch",age=6)
public void foo(){
System.out.println("еfoo");
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
// new Test().foo();
// List<String> list = new ArrayList();
TestProcessor.process("com.java.anotion.TestableAnnotationTest");
}
}示例二
package com.java.anotion;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
/**
* 带成员变量的注释
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ActionListenerFor {
String listener();
}
package com.java.anotion;
import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import javax.swing.AbstractButton;
public class ActionListenerInstaller {
public static void processAnnotations(Object obj){
Class cl = obj.getClass();
for(Field f : cl.getDeclaredFields()){
//讲指定Field设置为可自由访问的,避免private的不可访问
f.setAccessible(true);
//获取指定Filed的ActionListenerFor类型的注释
ActionListenerFor a = f.getAnnotation(ActionListenerFor.class);
if(a != null){
try {
Class listenerClass = Class.forName(a.listener());
ActionListener al = (ActionListener) listenerClass.newInstance();
AbstractButton ab = (AbstractButton) f.get(obj);
ab.addActionListener(al);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}package com.java.anotion;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class AnnotationTest {
private JFrame jFrame = new JFrame("使用注释绑定事件监听器");
//引用内部类的时候com.java.anotion.AnnotationTest$OkListenter反射不成功,报错
@ActionListenerFor(listener="com.java.anotion.OkListenter")
private JButton ok = new JButton("确定");
@ActionListenerFor(listener="com.java.anotion.CancelListenter")
private JButton cancel = new JButton("取消");
public void init(){
JPanel jp = new JPanel();
jp.add(ok);
jp.add(cancel);
jFrame.add(jp);
ActionListenerInstaller.processAnnotations(this);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setLocation(300, 200);
jFrame.setSize(500, 300);
jFrame.setVisible(true);
}
public static void main(String[] args) {
new AnnotationTest().init();
}
/*
class OkListenter implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "点击了确认按钮");
}
}
class CancelListenter implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "点击了取消按钮");
}
}
*/
}package com.java.anotion;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
public class CancelListenter implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "点击了取消按钮");
}
}package com.java.anotion;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
public class OkListenter implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "点击了确认按钮");
}
}JDK的元Annotation
在java.lang.annotation包下提供了四个元Annotation。
@Retention:用于指定该Annotation可以保留多长时间。包含一个RetentionPolicy类型的value成员变量。
value有三个值:
1、RetentionPolicy.CLASS 编译器将把注释记录在class文件中。当运行Java程序时,JVM不再保留注释。这是默认值。
2、RetentionPolicy.RUNTIME 编译器把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。
3、RetentionPolicy.SOURCE 编译器直接丢弃这种策略的注释。
注:如果Annotation类型只有一个value成员变量,可以直接写value值,不用name=value的形式。
@Target:用于指定该Annotation修饰哪些程序元素。也包含一个ElementType类型的value成员变量。
value值有8个:
ElementType.ANNOTATION_TYPE只能修饰Annotation
ElementType.CONSTRUCTOR修饰构造器
ElementType.FIELD修饰成员变量
ElementType.LOCAL_VARIABLE修饰局部变量
ElementType.METHOD修饰方法定义
ElementType.PACKAGE修饰包定义
ElementType.PARAMETER修饰参数
ElementType.TYPE修饰类、接口或枚举定义
@Documented: 用于呗javadoc工具提取成文档,如果注释了提前的文档说明会有注释,否则没有。
@Inherited:指定被它修饰的Annotation将具有继承性。基类如有@Inherited,继承此基类的类也有此annotation。
4、提高
使用APT处理Annotation
APT(annotation processing tool)是一种处理注释工具。它对源文件进行检测找出其中的Annotation后,使用Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其他的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译
生成的源代码文件和原来的源文件,将它们一起生成class文件。
使用APT主要目的是简化开发者的工作量,因为APT可以在编译程序源代码的同时,生成一些附属文件(比如源文件、类文件、程序发布描述文件等),这些附属文件的内容
也都是与源代码相关的。换句话说,使用APT可以代替传统的对代码信息和附属文件的维护工作。
为了是系统的APT工具读取源文件中的Annotation,必须自定义一个Annotation处理器,编写处理器需要JDK lib目录中的tools.jar里的如下四个包:
com.sun.mirror.apt:和APT交互的接口
com.sun.mirror.declaration:包含各种封装类成员、类方法、类声明的接口
com.sun.mirror.type:包含各种封装源代码中程序元素的接口。
com.sun.mirror.util:提供了用于处理类型和声明的一些工具。
对于APT的使用,比如hibernate自动生成的bean属性文件xxx.xxx.xml,实现原理就是APT。编写自己的Annotation,
在bean类中引用自定义Annotation,系统会调用APT生成响应的配置文件。
本文出自 “风云海滩” 博客,请务必保留此出处http://3950566.blog.51cto.com/3940566/1562371
原文:http://3950566.blog.51cto.com/3940566/1562371