Java :Annotation(注释)

来源:互联网 发布:阅面科技 知乎 编辑:程序博客网 时间:2024/05/16 13:41

Java :Annotation(注释)
本文地址:http://blog.csdn.net/shanglianlm/article/details/50350274

1 基本 Annotation

三个基本的 Annotation 如下:

  • @Override
  • @Deprecated
  • @SuppressWarnings

JDK 提供的三个基本 Annotation 都定义在 java.lang 包下。

1-1 限定重写父类方法:@Override

这里写图片描述

public class Fruit{    public void info()    {         System.out.println("水果的info方法...");    }}class Apple extends Fruit{    // 使用@Override指定下面方法必须重写父类方法    @Override     public void info()    {        System.out.println("苹果重写水果的info方法...");    }}

1-2 标示已过时:@Deprecated

这里写图片描述

class Apple{    // 定义info方法已过时    @Deprecated    public void info()    {        System.out.println("Apple的info方法");    }}public class DeprecatedTest{    public static void main(String[] args)     {        // 下面使用info方法时将会被编译器警告        new Apple().info();    }}

1-3 抑制编译器警告:@SuppressWarnings

这里写图片描述

// 关闭整个类里的编译器警告@SuppressWarnings(value="unchecked")public class SuppressWarningsTest{    public static void main(String[] args)     {        List<String> myList = new ArrayList();          }}

2 自定义 Annotation

2-1 定义 Annotation

这里写图片描述

// 定义一个简单的 Annotation 类型public @interface Test{}

修饰类

// 使用 @Test 修饰类定义@Testpublic class MyClass{   ...}

修饰方法

public class MyClass{   // 使用 @Test 修饰方法   @Test   public void info()   {     ...    } }

添加成员变量

public @interface MyTag{   // 定义了两个成员变量的 Annotation   String name();   int age();}

使用

public class Test{   // 使用带成员变量的 Annotation 时,需要为成员变量赋值   @MyTag(name="xx", age=6)   public void info()   {      ...    }}
public @interface MyTag{    //使用 default 为两个成员变量指定初始值    String name() default "yeeku";    int age() default 32;}

使用

public class Test{   //   @MyTag   public void info()   {     ...   }}

2-2 提取 Annotation 的信息

这里写图片描述
这里写图片描述
这里写图片描述

// 获取 Test 类的 info 方法的所有注释Annotation [] aArray = Class.forName("Test").getMethod("info").getAnnotations();// 遍历所有注释for(Annotation an:aArray){   System.out.println(an);}

这里写图片描述

// 获取 tt 对象的 info 方法所包含的所有注释Annotation [] annotation = tt.getClass().getMethod("info").getAnnotations();// 遍历每个注释对象for(Annotation tag:annotation){   // 如果 tag 注释是 MyTag1 类型   if(tag instanceof MyTag1)   {       System.out.println("Tag is: " + tag);       // 将 tag 强制类型转换为 MyTag1, 并调用 tag 对象的 method1 和 method2 两个方法       System.out.println("tag.name(): " + ((MyTag1)tag).method1());       System.out.println("tag.age(): " + ((MyTag1)tag).method2());   }   // 如果 tag 注释是 MyTag2 类型   if(tag instanceof MyTag2)   {       System.out.println("Tag is: " + tag);       // 将 tag 强制类型转换为 MyTag2, 并调用 tag 对象的 method1 和 method2 两个方法       System.out.println("tag.name(): " + ((MyTag2)tag).method1());       System.out.println("tag.age(): " + ((MyTag2)tag).method2());   }}

2-3 使用 Annotation 的例子

两个例子
1. 没有任何成员变量的 Annotation,,用于标识哪些方法是可测试的。
Testable.java

//使用JDK的元数据Annotation:Retention@Retention(RetentionPolicy.RUNTIME)// 使用JDK的元数据Annotation:Target@Target(ElementType.METHOD)// 定义一个标记注释,不包含任何成员变量,即不可传入元数据public @interface Testable {}

MyTest.java

public class MyTest{    // 使用@Testable标记注释指定该方法是可测试的    @Testable    public static void m1()     {    }    public static void m2()     {    }    // 使用@Testable标记注释指定该方法是可测试的    @Testable    public static void m3()     {        throw new RuntimeException("Boom");      }    public static void m4()    {    }    // 使用@Testable标记注释指定该方法是可测试的    @Testable    public static void m5()    {    }    public static void m6()    {    }    // 使用@Testable标记注释指定该方法是可测试的    @Testable    public static void m7()    {        throw new RuntimeException("Crash");       }    public static void m8()    {    }}

ProcessorTest.java

public class ProcessorTest{    public static void process(String clazz)        throws ClassNotFoundException    {        int passed = 0;        int failed = 0;        // 遍历clazz对应的类里的所有方法        for (Method m : Class.forName(clazz).getMethods())        {            // 如果该方法使用了@Testable修饰            if (m.isAnnotationPresent(Testable.class))            {                try                {                    // 调用m方法                    m.invoke(null);                    // passed加1                    passed++;                }                catch (Exception ex)                {                    System.out.println("方法" + m + "运行失败,异常:"                        + ex.getCause());                    failed++;                }            }        }        //统计测试结果        System.out.println("共运行了:" + (passed + failed)            + "个方法,其中:\n" + "失败了:" + failed + "个,\n"            + "成功了:" + passed + "个!");    }}

RunTests.java

public class RunTests{    public static void main(String[] args)        throws Exception     {        //处理MyTest类        ProcessorTest.process("MyTest");    }}

运行结果:
这里写图片描述

  1. 通过 ActionListenerFor Annotation 来为程序中的按钮绑定事件监听器

ActionListenerFor.java

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

AnnotationTest.java

public class AnnotationTest{    private JFrame mainWin = new JFrame("使用注释绑定事件监听器");    // 使用Annotation为ok按钮绑定事件监听器    @ActionListenerFor(listener=OkListener.class)    private JButton ok = new JButton("确定");    // 使用Annotation为cancel按钮绑定事件监听器    @ActionListenerFor(listener=CancelListener.class)    private JButton cancel = new JButton("取消");    public void init()    {        // 初始化界面的方法        JPanel jp = new JPanel();        jp.add(ok);        jp.add(cancel);        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();    }}// 定义ok按钮的事件监听器实现类class OkListener implements ActionListener{    public void actionPerformed(ActionEvent evt)    {        JOptionPane.showMessageDialog(null , "单击了确认按钮");    }}// 定义cancel按钮的事件监听器实现类class CancelListener implements ActionListener{    public void actionPerformed(ActionEvent evt)    {        JOptionPane.showMessageDialog(null , "单击了取消按钮");    }}

ActionListenerInstaller.java

public class ActionListenerInstaller{    // 处理Annotation的方法,其中obj是包含Annotation的对象    public static void processAnnotations(Object obj)    {        try        {            // 获取obj对象的类            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注释里的元数据listner(它是一个监听器类)                    Class<? extends ActionListener> listenerClazz = a.listener();                    // 使用反射来创建listner类的对象                    ActionListener al = listenerClazz.newInstance();                    AbstractButton ab = (AbstractButton)fObj;                    // 为ab按钮添加事件监听器                    ab.addActionListener(al);                }            }        }        catch (Exception e)        {            e.printStackTrace();        }    }}

运行结果:
这里写图片描述

3 JDK 的元 Annotation

这里写图片描述

3-1 使用 @Retention

这里写图片描述

// 定义下面的 Testable Annotation 的保留到运行时@Retention(value = RetentionPoicy.RUNTIME)public @interface Testable{}

// 定义下面的 Testable Annotation 将被编译器直接丢弃@Retention(RetentionPoicy.SOURCE)public @interface Testable{}

3-2 使用 @Target

这里写图片描述

@Target(ElementType.FIELD)public @interface ActionListenerFor{}

3-3 使用 @Documented

这里写图片描述
Testable.java

// 定义下面的 Testable Annotation 将被编译器直接丢弃@Retention(RetentionPoicy.RUNTIME)@Target(ElementType.METHOD)// 定义 Testable Annotation 将被 javadoc 工具提取@Documentedpublic @interface Testable{}

MyTest.java

public class MyTest{    // 使用 @Test 修饰 info 方法    @Testable    public void info()     {       System.out.println("info 方法...");    }}

3-4 使用 @Inherited

这里写图片描述

Inheritable.java

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface Inheritable{}

TestInheritable.java

// 使用@Inheritable修饰的Base类@Inheritableclass Base{}// TestInheritable类只是继承了Base类,// 并未直接使用@Inheritable Annotiation修饰public class InheritableTest extends Base{    public static void main(String[] args)    {        // 打印TestInheritable类是否具有@Inheritable修饰        System.out.println(InheritableTest.class             .isAnnotationPresent(Inheritable.class));    }}

4 使用 APT 处理 Annotation

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

Persistent.java

@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)@Documentedpublic @interface Persistent{    String table();}

这里写图片描述

IdProperty.java

@Target(ElementType.FIELD)@Retention(RetentionPolicy.SOURCE)@Documentedpublic @interface IdProperty{    String column();    String type();    String generator();}

Property.java

@Target(ElementType.FIELD)@Retention(RetentionPolicy.SOURCE)@Documentedpublic @interface Property{    String column();    String type();}

Person.java

@Persistent(table="person_inf")public class Person{    @IdProperty(column="person_id",type="integer",generator="identity")    private int id;    @Property(column="person_name",type="string")    private String name;    @Property(column="person_age",type="integer")    private int age;    //无参数的构造器    public Person()    {    }    //初始化全部成员变量的构造器    public Person(int id , String name , int age)    {        this.id = id;        this.name = name;        this.age = age;    }    // 下面省略各Field的setter和getter方法    ... }

为上面三个 Annotation 提供一个 Annotation 处理器,该处理器的功能是根据注释来生成一个 Hibernate 映射文件。
HibernateAnnotationProcessor.java

@SupportedSourceVersion(SourceVersion.RELEASE_7)// 指定可处理@Persistent、@Id、@Property三个Annotation@SupportedAnnotationTypes({"Persistent" , "Id" , "Property"})public class HibernateAnnotationProcessor    extends AbstractProcessor{    // 循环处理每个需要处理的程序对象    public boolean process(Set<? extends TypeElement> annotations        , RoundEnvironment roundEnv)    {        // 定义一个文件输出流,用于生成额外的文件        PrintStream ps = null;        try        {            // 遍历每个被@Persistent修饰的class文件            for (Element t : roundEnv.getElementsAnnotatedWith(Persistent.class))            {                // 获取正在处理的类名                Name clazzName = t.getSimpleName();                // 获取类定义前的@Persistent Annotation                Persistent per = t.getAnnotation(Persistent.class);                // 创建文件输出流                ps = new PrintStream(new FileOutputStream(clazzName                    + ".hbm.xml"));                // 执行输出                ps.println("<?xml version=\"1.0\"?>");                ps.println("<!DOCTYPE hibernate-mapping PUBLIC");                ps.println("    \"-//Hibernate/Hibernate "                    + "Mapping DTD 3.0//EN\"");                ps.println("    \"http://www.hibernate.org/dtd/"                    + "hibernate-mapping-3.0.dtd\">");                ps.println("<hibernate-mapping>");                ps.print("  <class name=\"" + t);                // 输出per的table()的值                ps.println("\" table=\"" + per.table() + "\">");                for (Element f : t.getEnclosedElements())                {                    // 只处理Field上的Annotation                    if (f.getKind() == ElementKind.FIELD)   //①                    {                        // 获取Field定义前的@Id Annotation                        Id id = f.getAnnotation(Id.class);      //②                        // 当@Id Annotation存在时输出<id.../>元素                        if(id != null)                        {                            ps.println("        <id name=\""                                + f.getSimpleName()                                + "\" column=\"" + id.column()                                + "\" type=\"" + id.type()                                + "\">");                            ps.println("        <generator class=\""                                + id.generator() + "\"/>");                            ps.println("        </id>");                        }                        // 获取Field定义前的@Property Annotation                        Property p = f.getAnnotation(Property.class);  //③                        // 当@Property Annotation存在时输出<property.../>元素                        if (p != null)                        {                            ps.println("        <property name=\""                                + f.getSimpleName()                                + "\" column=\"" + p.column()                                + "\" type=\"" + p.type()                                + "\"/>");                        }                    }                }                ps.println("    </class>");                ps.println("</hibernate-mapping>");            }        }        catch (Exception ex)        {            ex.printStackTrace();        }        finally        {            if (ps != null)            {                try                {                    ps.close();                }                catch (Exception ex)                {                    ex.printStackTrace();                }            }        }        return true;    }}

这里写图片描述
HibernateAnnotationFactory.java

public class HibernateAnnotaionFactory implements AnnotationProcessorFactory{   // 所有支持的注释类型   public Collection<String> supportedAnnotationTypes()   {      return Arrays.asList("Property", "IdProperty", "Persistent");   }   // 返回所有支持的选项   public Collection<String> supportedOption()   {      return Arrays.asList(new String[0]);   }   // 返回 Annotation 处理器   public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env)   {       return new HibernateAnnotationProcessor(env);   }}

提供上述工厂后,就可以使用 APT 工具来处理上面的 源文件,并据此生成一份 xml 文件。

APT 工具位于 JDK 安装路径的 bin 目录下。

rem HibernateAnnotationFactory Person.java Annotationapt -factory HibernateAnnotationFactory Person.java

生成的 Person.hbm.xml 文件

<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping>    <class name="Person" table="person_inf">        <id name="id" column="person_id" type="integer">        <generator class="identity"/>        </id>        <property name="name" column="person_name" type="string"/>        <property name="age" column="person_age" type="integer"/>    </class></hibernate-mapping>
0 0