关注JEECG发展历程 关注最新动态和版本, 记录JEECG成长点滴 更新日志 - 技术支持 - 招聘英才

JEECG最新版本下载 JEECG智能开发平台 - 显著提高开发效率 常见问题 - 入门视频 - 参与开源团队

商务QQ: 69893005、418799587 商务热线(5*8小时): 010-64808099 官方邮箱: jeecgos@163.com

查看: 13073|回复: 0

在运行时使用反射获取注解

[复制链接]
发表于 2013-3-22 09:44:54 | 显示全部楼层 |阅读模式
尽管设计注解的目的主要是用于其他的开发和部署工具,但是如果为注解指定RUNTIME保留策略,那么任何程序在运行时都可以使用反射来查询注解。反射是能够在运行时获取类相关信息的特性。反射API位于java.lang.reflect包中。使用反射的方式有很多,在此不可能解释所有这些方式。但是,我们将分析应用了注解的几个例子。
使用反射的第一步是获取Class对象,表示希望获取其中注解的类。Class是Java的内置类,是在java.lang包中定义的。在本书第Ⅱ部分将对这个包进行详细介绍。可以使用多种方式来获取Class对象。其中最简单的方式是调用getClass()方法,该方法是由Object类定义的,它的一般形式如下所示:

final Class<?> getClass( )

该方法返回用来表示调用对象的Class对象。
注意:
注意上面显示的getClass()方法声明中跟在Class后面的<?>,这与Java中的泛型特性有关。在本章讨论的getClass()方法以及其他几个与反射有关的方法,需要使用泛型。泛型将在第14章介绍。但是,理解反射的基本原则不需要先理解泛型。
获得Class对象后,可以使用其他方法获取与类声明中各个条目相关的信息,包括注解。如果希望获取与类声明中特定条目关联的注解,那么首先必须获取表示该特定条目的对象。
例如,Class提供了getMethod()、getField()以及getConstructor()方法(还有其他方法),这些方法分别获取与方法、域变量以及构造函数相关的信息,这些方法返回Method、Field 以及Constructor类型的对象。
为了理解这个过程,分析一个获取与方法关联的注解的例子。为此,首先获取表示类的Class对象,然后调用Class对象的getMethod()方法并指定方法的名称。getMethod()方法的一般形式如下:

Method getMethod(String methName, Class<?> ... paramTypes)

方法的名称被传递到methName中。如果方法有参数,那么必须通过paramTypes指定表示这些参数类型的Class对象。注意paramTypes是可变长度参数,这意味着可以指定需要的任意多个参数,包括指定0个参数。getMethod()方法返回表示方法的Method对象。如果没有找到方法,就抛出NoSuchMethodException异常。
对Class、Method、Field以及Constructor对象调用getAnnotation()方法,可以获得与对象关联的特定信息。该方法的一般形式如下:

<A extends Annotation> getAnnotation(Class<A> annoType)

其中,annoType是表示您感兴趣注解的Class对象。该方法返回对注解的一个引用,使用这个引用可以获取与注解成员关联的值。如果没有找到注解,该方法会返回null。如果注解的保留策略不是RUNTIME,就会出现这种情况。
下面的程序总结了在前面介绍的所有内容,并使用反射显示与某个方法关联的注解:

import java.lang.annotation.*;
import java.lang.reflect.*;

// An annotation type declaration.
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno {
  String str();
  int val();
}

class Meta {
  // Annotate a method.
  @MyAnno(str = "Annotation Example", val = 100)
  public static void myMeth() {
    Meta ob = new Meta();

    // Obtain the annotation for this method
    // and display the values of the members.
    try {
      // First, get a Class object that represents
      // this class.
      Class<?> c = ob.getClass();

      // Now, get a Method object that represents
      // this method.
      Method m = c.getMethod("myMeth");

      // Next, get the annotation for this class.
      MyAnno anno = m.getAnnotation(MyAnno.class);

      // Finally, display the values.
      System.out.println(anno.str() + " " + anno.val());
    } catch (NoSuchMethodException exc) {
      System.out.println("Method Not Found.");
    }
  }

  public static void main(String args[]) {
    myMeth();
  }
}

该程序的输出如下所示:

Annotation Example 100

这个程序使用前面介绍的反射,获取并显示与Meta类中myMeth()方法关联的MyAnno注解中str 和val 的值。有两点需要特别注意。第一点,注意下面这行代码中的表达MyAnno.class:

MyAnno anno = m.getAnnotation(MyAnno.class);

对这个表达式求值的结果是表示MyAnno类型的Class对象,即注解。这种结构被称为“类字面值”。无论何时,当需要已知类的Class对象时,就可以使用这类表达式。例如,可以使用下面这条语句获取Meta的Class对象:

Class<?> c = Meta.class;
当然,只有当事先知道对象的类名时才能使用这种方式,但我们并不总是知道对象的类名。通常,可以获取类、接口、基本类型以及数组的类字面值(记住,<?>语法与Java的泛型特性有关,泛型将在第14章介绍)。
需要注意的第二点是,当通过下面这行代码进行输出时,如何获取与str和val关联的数值:

System.out.println(anno.str() + " " + anno.val());

注意这里使用方法调用语法来调用它们。当需要注解成员的值时,可以使用相同的方式。
1. 第二个反射示例
在前面的例子中,myMeth()方法没有参数。因此,当调用getMethod()方法时,只传递名称myMeth。但是,为了获取带有参数的方法,必须指定表示参数类型的类对象作为getMethod()方法的参数。例如,下面的程序与前面的程序稍微有些区别:

import java.lang.annotation.*;
import java.lang.reflect.*;

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno {
  String str();
  int val();
}

class Meta {

  // myMeth now has two arguments.
  @MyAnno(str = "Two Parameters", val = 19)
  public static void myMeth(String str, int i)
  {
    Meta ob = new Meta();

    try {
      Class<?> c = ob.getClass();

      // Here, the parameter types are specified.
      Method m = c.getMethod("myMeth", String.class, int.class);

      MyAnno anno = m.getAnnotation(MyAnno.class);

      System.out.println(anno.str() + " " + anno.val());
    } catch (NoSuchMethodException exc) {
       System.out.println("Method Not Found.");
    }
  }

  public static void main(String args[]) {
    myMeth("test", 10);
  }
}

该版本的输出如下所示:

Two Parameters 19

在这个版本中,myMeth()方法带有一个String参数和一个int 参数。为了获取关于这个方法的信息,必须以如下方式调用getMethod()方法:

Method m = c.getMethod("myMeth", String.class, int.class);

在此,作为附加参数传递表示String和int 类型的Class对象。
2. 获取所有注解
可以获取与某个条目关联的具有RUNTIME保留策略的所有注解,具体方法是为该条目调用getAnnotations()方法。该方法的一般形式如下:

Annotation[ ] getAnnotations( )

上述方法返回一个注解数组。可以针对Class、Method、Constructor以及Field类型的对象调用getAnnotations()方法。
下面是另外一个使用反射的例子,该例显示了如何获取与类和方法关联的所有注解。该例声明了两个注解。然后使用这两个注解来注解类和方法。

// Show all annotations for a class and a method.
import java.lang.annotation.*;
import java.lang.reflect.*;

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno {
  String str();
  int val();
}

@Retention(RetentionPolicy.RUNTIME)
@interface What {
  String description();
}

@What(description = "An annotation test class")
@MyAnno(str = "Meta2", val = 99)
class Meta2 {

  @What(description = "An annotation test method")
  @MyAnno(str = "Testing", val = 100)
  public static void myMeth() {
    Meta2 ob = new Meta2();

    try {
      Annotation annos[] = ob.getClass().getAnnotations();

      // Display all annotations for Meta2.
      System.out.println("All annotations for Meta2:");
      for(Annotation a : annos)
        System.out.println(a);

      System.out.println();

    // Display all annotations for myMeth.
    Method m = ob.getClass( ).getMethod("myMeth");
    annos = m.getAnnotations();

    System.out.println("All annotations for myMeth:");
    for(Annotation a : annos)
      System.out.println(a);
    } catch (NoSuchMethodException exc) {
       System.out.println("Method Not Found.");
    }
  }

  public static void main(String args[]) {
    myMeth();
  }
}

输出如下所示:

All annotations for Meta2:
@What(description=An annotation test class)
@MyAnno(str=Meta2, val=99)

All annotations for myMeth:
@What(description=An annotation test method)
@MyAnno(str=Testing, val=100)

该程序使用getAnnotations()方法来获取与类Meta2和方法myMeth()相关联的所有注解,并将它们保存到数组中。正如前面所解释的,getAnnotations()方法返回Annotation对象的数组。回忆一下,Annotation是所有注解接口的超接口,并且它重载了Object类中的toString()方法。因此,当输出对Annotation的引用时,会调用toString()方法来生成描述注解的字符串,如前面的输出所示。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表