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