精华内容
参与话题
问答
  • Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。 Java 的这一...
    Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。
    Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。

      JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。

    1. 一个简单的例子

      考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。


    import java.lang.reflect.*;
    public class DumpMethods {
    public static void main(String args[]) {
    try {
    Class c = Class.forName(args[0]);
    Method m[] = c.getDeclaredMethods();
    for (int i = 0; i < m.length; i++)
    System.out.println(m[i].toString());
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      按如下语句执行:

    java DumpMethods java.util.Stack


      它的结果输出为:

    public java.lang.Object java.util.Stack.push(java.lang.Object)

    public synchronized java.lang.Object java.util.Stack.pop()

    public synchronized java.lang.Object java.util.Stack.peek()

    public boolean java.util.Stack.empty()

    public synchronized int java.util.Stack.search(java.lang.Object)


      这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。

      这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。


    2.开始使用 Reflection

      用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。
    下面就是获得一个 Class 对象的方法之一:

    Class c = Class.forName("java.lang.String");


      这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:

    Class c = int.class;


      或者

    Class c = Integer.TYPE;


      它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。

      第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。

      一旦取得这个信息,就可以进行第三步了——使用 reflection API 来操作这些信息,如下面这段代码:

    Class c = Class.forName("java.lang.String");

    Method m[] = c.getDeclaredMethods();

    System.out.println(m[0].toString());


      它将以文本方式打印出 String 中定义的第一个方法的原型。

      在下面的例子中,这三个步骤将为使用 reflection 处理特殊应用程序提供例证。

    模拟 instanceof 操作符

      得到类信息之后,通常下一个步骤就是解决关于 Class 对象的一些基本的问题。例如,Class.isInstance 方法可以用于模拟 instanceof 操作符:

    class A {
    }

    public class instance1 {
    public static void main(String args[]) {
    try {
    Class cls = Class.forName("A");
    boolean b1 = cls.isInstance(new Integer(37));
    System.out.println(b1);
    boolean b2 = cls.isInstance(new A());
    System.out.println(b2);
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      在这个例子中创建了一个 A 类的 Class 对象,然后检查一些对象是否是 A 的实例。Integer(37) 不是,但 new A()是。


    3.找出类的方法

      找出一个类中定义了些什么方法,这是一个非常有价值也非常基础的 reflection 用法。下面的代码就实现了这一用法:

    import java.lang.reflect.*;

    public class method1 {
    private int f1(Object p, int x) throws NullPointerException {
    if (p == null)
    throw new NullPointerException();
    return x;
    }

    public static void main(String args[]) {
    try {
    Class cls = Class.forName("method1");
    Method methlist[] = cls.getDeclaredMethods();
    for (int i = 0; i < methlist.length; i++) {
    Method m = methlist[i];
    System.out.println("name = " + m.getName());
    System.out.println("decl class = " + m.getDeclaringClass());
    Class pvec[] = m.getParameterTypes();
    for (int j = 0; j < pvec.length; j++)
    System.out.println("param #" + j + " " + pvec[j]);
    Class evec[] = m.getExceptionTypes();
    for (int j = 0; j < evec.length; j++)
    System.out.println("exc #" + j + " " + evec[j]);
    System.out.println("return type = " + m.getReturnType());
    System.out.println("-----");
    }
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      这个程序首先取得 method1 类的描述,然后调用 getDeclaredMethods 来获取一系列的 Method 对象,它们分别描述了定义在类中的每一个方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods,你还能获得继承来的各个方法的信息。
    取得了 Method 对象列表之后,要显示这些方法的参数类型、异常类型和返回值类型等就不难了。这些类型是基本类型还是类类型,都可以由描述类的对象按顺序给出。

      输出的结果如下:

    name = f1

    decl class = class method1


    param #0 class java.lang.Object

    param #1 int

    exc #0 class java.lang.NullPointerException

    return type = int

    -----
    name = main

    decl class = class method1

    param #0 class [Ljava.lang.String;

    return type = void


    4.获取构造器信息

      获取类构造器的用法与上述获取方法的用法类似,如:

    import java.lang.reflect.*;

    public class constructor1 {
    public constructor1() {
    }

    protected constructor1(int i, double d) {
    }

    public static void main(String args[]) {
    try {
    Class cls = Class.forName("constructor1");
    Constructor ctorlist[] = cls.getDeclaredConstructors();
    for (int i = 0; i < ctorlist.length; i++) {
    Constructor ct = ctorlist[i];
    System.out.println("name = " + ct.getName());
    System.out.println("decl class = " + ct.getDeclaringClass());
    Class pvec[] = ct.getParameterTypes();
    for (int j = 0; j < pvec.length; j++)
    System.out.println("param #" + j + " " + pvec[j]);
    Class evec[] = ct.getExceptionTypes();
    for (int j = 0; j < evec.length; j++)
    System.out.println("exc #" + j + " " + evec[j]);
    System.out.println("-----");
    }
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      这个例子中没能获得返回类型的相关信息,那是因为构造器没有返回类型。

      这个程序运行的结果是:

    name = constructor1

    decl class = class constructor1

    -----
    name = constructor1

    decl class = class constructor1

    param #0 int

    param #1 double


    5.获取类的字段(域)
    找出一个类中定义了哪些数据字段也是可能的,下面的代码就在干这个事情:

    import java.lang.reflect.*;

    public class field1 {
    private double d;
    public static final int i = 37;
    String s = "testing";


    public static void main(String args[]) {
    try {
    Class cls = Class.forName("field1");
    Field fieldlist[] = cls.getDeclaredFields();
    for (int i = 0; i < fieldlist.length; i++) {
    Field fld = fieldlist[i];
    System.out.println("name = " + fld.getName());
    System.out.println("decl class = " + fld.getDeclaringClass());
    System.out.println("type = " + fld.getType());
    int mod = fld.getModifiers();
    System.out.println("modifiers = " + Modifier.toString(mod));
    System.out.println("-----");
    }
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier,它也是一个 reflection 类,用来描述字段成员的修饰语,如“private int”。这些修饰语自身由整数描述,而且使用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。这个程序的输出是:

    name = d

    decl class = class field1

    type = double

    modifiers = private

    -----
    name = i

    decl class = class field1

    type = int

    modifiers = public static final

    -----
    name = s

    decl class = class field1

    type = class java.lang.String

    modifiers =


      和获取方法的情况一下,获取字段的时候也可以只取得在当前类中申明了的字段信息 (getDeclaredFields),或者也可以取得父类中定义的字段 (getFields) 。


    6.根据方法的名称来执行方法

      文本到这里,所举的例子无一例外都与如何获取类的信息有关。我们也可以用 reflection 来做一些其它的事情,比如执行一个指定了名称的方法。下面的示例演示了这一操作:

    import java.lang.reflect.*;
    public class method2 {
    public int add(int a, int b) {
    return a + b;
    }
    public static void main(String args[]) {
    try {
    Class cls = Class.forName("method2");
    Class partypes[] = new Class[2];
    partypes[0] = Integer.TYPE;
    partypes[1] = Integer.TYPE;
    Method meth = cls.getMethod("add", partypes);
    method2 methobj = new method2();
    Object arglist[] = new Object[2];
    arglist[0] = new Integer(37);
    arglist[1] = new Integer(47);
    Object retobj = meth.invoke(methobj, arglist);
    Integer retval = (Integer) retobj;
    System.out.println(retval.intValue());
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }

    假如一个程序在执行的某处的时候才知道需要执行某个方法,这个方法的名称是在程序的运行过程中指定的 (例如,JavaBean 开发环境中就会做这样的事),那么上面的程序演示了如何做到。

      上例中,getMethod用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的Method 对象之后,在正确的对象实例中执行它。执行该方法的时候,需要提供一个参数列表,这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象,它封装了返回值 84。


    7.创建新的对象

      对于构造器,则不能像执行方法那样进行,因为执行一个构造器就意味着创建了一个新的对象 (准确的说,创建一个对象的过程包括分配内存和构造对象)。所以,与上例最相似的例子如下:

    import java.lang.reflect.*;

    public class constructor2 {
    public constructor2() {
    }

    public constructor2(int a, int b) {
    System.out.println("a = " + a + " b = " + b);
    }

    public static void main(String args[]) {
    try {
    Class cls = Class.forName("constructor2");
    Class partypes[] = new Class[2];
    partypes[0] = Integer.TYPE;
    partypes[1] = Integer.TYPE;
    Constructor ct = cls.getConstructor(partypes);
    Object arglist[] = new Object[2];
    arglist[0] = new Integer(37);
    arglist[1] = new Integer(47);
    Object retobj = ct.newInstance(arglist);
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      根据指定的参数类型找到相应的构造函数并执行它,以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象,而不是在编译的时候创建对象,这一点非常有价值。


    8.改变字段(域)的值

      reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它,下面的例子可以说明这一点:

    import java.lang.reflect.*;

    public class field2 {
    public double d;

    public static void main(String args[]) {
    try {
    Class cls = Class.forName("field2");
    Field fld = cls.getField("d");
    field2 f2obj = new field2();
    System.out.println("d = " + f2obj.d);
    fld.setDouble(f2obj, 12.34);
    System.out.println("d = " + f2obj.d);
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      这个例子中,字段 d 的值被变为了 12.34。


    9.使用数组

      本文介绍的 reflection 的最后一种用法是创建的操作数组。数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:

    import java.lang.reflect.*;

    public class array1 {
    public static void main(String args[]) {
    try {
    Class cls = Class.forName("java.lang.String");
    Object arr = Array.newInstance(cls, 10);
    Array.set(arr, 5, "this is a test");
    String s = (String) Array.get(arr, 5);
    System.out.println(s);
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。

      下面这段代码提供了一个更复杂的例子:

    import java.lang.reflect.*;

    public class array2 {
    public static void main(String args[]) {
    int dims[] = new int[]{5, 10, 15};
    Object arr = Array.newInstance(Integer.TYPE, dims);
    Object arrobj = Array.get(arr, 3);
    Class cls = arrobj.getClass().getComponentType();
    System.out.println(cls);
    arrobj = Array.get(arrobj, 5);
    Array.setInt(arrobj, 10, 37);
    int arrcast[][][] = (int[][][]) arr;
    System.out.println(arrcast[3][5][10]);
    }
    }


      例中创建了一个 5 x 10 x 15 的整型数组,并为处于 [3][5][10] 的元素赋了值为 37。注意,多维数组实际上就是数组的数组,例如,第一个 Array.get 之后,arrobj 是一个 10 x 15 的数组。进而取得其中的一个元素,即长度为 15 的数组,并使用 Array.setInt 为它的第 10 个元素赋值。

      注意创建数组时的类型是动态的,在编译时并不知道其类型。
    展开全文
  • 反射(Reflection)详解

    2018-07-18 16:46:33
    Reflection 产生原因:运行时获取、修改数据(类信息、成员数据信息)。 反射入口 java.lang.Class Class对象分为基本类型(8种)、引用类型:(具体内容在文章末尾) JVM会为每个对象(基础和引用类型)实例化一...

    Reflection

    产生原因:运行时获取、修改数据(类信息、成员数据信息)。

    反射入口 java.lang.Class

    Class对象分为基本类型(8种)、引用类型:(具体内容在文章末尾)

    JVM会为每个对象(基础和引用类型)实例化一个java.lang.Class实例。通过这个Class对象,可以在运行时访问对象的属性和类信息、也可以创建新的对象和类。反射的实现,首先要获取Class实例。

    获取Class实例的方法:

    • Object.getClass();
      Class class = object.getClass();
      枚举类型的对象获取的是枚举类的Class,数组对象获取的是数组元素的Class
    • .class
      Objcet.class; 
    
      int.class.newInstance()
    • Class.forName().
      只能用于引用类型,需要类的完整路径:如java.lang.String
    
      Class<?> class = Class.forName("java.lang.String");
    • static属性TYPE
      Class<String> class = String.TYPE;
      TYPE的定义:public static final Class<String> TYPE = (Class<String>) String[].class.getComponentType();

    获取相关类的Class对象:

    • 父类 getSuperclass()
    • 公共类、接口、枚举等 getClasses()
    • 显示申明的所有类、接口、枚举 getDeclaredClasses()
    • 成员声明类 getDeclaringClasses()

    Class对象获取类的信息:

    • 获取类的修饰符(修饰符介绍见本文末)
    
      int modifier = class.getModifiers() ;
    
      String modifierStr = Modifier.toString(int modifiers);
    
      同时因为Field继承了AnnotatedElement,所以在运行时可以获取注解,获取的注解必须是RUNTIME所修饰的注解

    获取类成员(Member)

    • 构造函数(Constructor)
    getDeclaredConstructor(Class<?>... parameterTypes)获取指定的构造方法,可获取私有方法
    getConstructor(Class<?>... parameterTypes) 获取指定的构造方法,不可获取私有方法
    getDeclaredConstructors() 获取所有构造方法,可获取私有方法
    getConstructor(Class<?>... parameterTypes) 获取所有构造方法,不可获取私有方法
    • 成员变量(Field)

    获取变量:

    getDeclaredField(String name);// 获取指定的变量   类自有变量  包括取私有变量,但是不能获取继承父类的
    
    getField(String name);  //获取指定的变量,可获取自身和继承自父类的public变量。
    
    getDeclaredFields(); //获取类自有的所有变量集合,包括私有变量,不包括继承变量
    
    getFields(); //获取自身和继承自父类的public变量的集合。

    获取变量类型:

    Field.getType() :返回Class对象  ,这个变量的类型
    Field.getGenericType():返回Type接口  ,如果类型是泛型,getGenericType可以得到这个泛型的参数类型(T),getType()只能得到这个类型的接口类型(Object)。

    设置变量的值

    Class cls = object.getClass();
    
    Field field = objcet.getField("name");
    
    field.set(object,"new Name");
    
    //修改变量的值时,编译器不会拆装箱操作,设置时必须设置相同的类型
    
    Integer itg;
    
    Class cls = object.getClass();
    
    Field field = objcet.getField("itg");
    
    field.set(object,new Integer(666));
    
    //直接修改final类型的变量会报错`IllegalAccessException` 需要setAccessible(true),
    //就可以访问修改了。(由于 `Field` 继承自 `AccessibleObject` , 我们可以使用 
    //`AccessibleObject.setAccessible()` 方法告诉安全机制,这个变量可以访问。 )
    • 成员方法(Method:java.lang.reflect.Method)
    //获取的方法属性同成员变量。
    getDeclaredMethod(String name,Class parameterTypes.....)
    getMethod(String name,Class parameterTypes.....)
    getDeclaredMethods()
    getMethods()
    

    继承于父类的方法因强制执行无法被反射,因此反射方法仅限本类方法。

    反射方法得到的数据由:修饰符、返回值、参数、注解、抛出异常五点组成:

    getName());  //获得单独的方法名 
    
    toGenericString()); //获得完整的方法信息(包括修饰符、返回值、路径、名称、参数、抛出值) 
    
    int modifiers = declaredMethod.getModifiers();Modifier.toString(modifiers)    /获得修饰符 
    
    getReturnType();   //获得返回值 
    
    getGenericReturnType();//获得完整信息的返回值 
    
    //获得参数类型 
    Class<?>[] parameterTypes = method.getParameterTypes(); 
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    
    //获得异常名称 
    Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();   
    Type[] genericExceptionTypes = declaredMethod.getGenericExceptionTypes();   
    
     //获得注解 
    Annotation[] annotations = declaredMethod.getAnnotations(); 
    annotations[i].annotationType();

    三种特殊的方法类型:

    synthetic(合成,内部类访问权限), varagrs(参数可变), bridge(桥接,泛型相关)
    

    反射 调用方法

    invoke() ;
    //java.lang.reflect.Method.invoke(Object receiver,Object... args)
    //该方法抛异常:java.lang.reflect.InvocationTargetException
    receiver 是方法所属于的对象,静态方法可直接传null,args传方法的参数
    //当访问private属性的方法时,需要设置setAccessible(true) 否则报`IllegalAccessException`异常
    
    

    相似概念:Introspection(自省):

    区别:

    Introspection 是在运行时获取对象的信息,Reflection是获取或者修改对象的信息
    Introspection多用于 instanceof 的判断,Reflection多用来实现访问私有方法,创建对象等
    总的说,Reflection是多了可进行修改信息。
    

    附录 :

    Class对象的基本类型(8种)、引用类型:

    基本类型:

    • 整型:默认类型int

      • byte : 8位、有符号的二进制补码表示整数、范围-128~127(-2^7~2^7-1)
      • short : 16位、有符号的二进制补码表示整数、范围-32768~32767(-2^15~2^15-1)
      • int : 32位、有符号的二进制补码表示整数、范围-214748348~2147483647(-2^31~2^31-1)
      • long : 64位 、 范围-9223372036854775808~9223372036854775807(-2^63~2^63-1) 。
        long a =1000000000000L;
    • 浮点类型:默认类型double

      • float : 单精度、32位浮点数,存储大型浮点数组较为节省空间、不能用来表示精确值如货币
      • double : 双精度、64位、也不可表示精确的数。
    • bool值
      boolean : true/false、 默认值false

    • 字符型

      • char : 单一的16位Unicode字符、最大值0(\u0000)、最大值65535(\uffff) 。char letter=”A”;

    引用类型:

    • 引用类型都是继承自java.lang.Object
    • 类、枚举、数组、接口
    • java.io.Serializable接口及其实现类、包装类Double、Integer。

    修饰符Modifier

    修饰符用来定义类、方法、变量,通常在语句首位,分为两大类:访问修饰符和非访问修饰符

    访问修饰符:

    default:

        缺省值,不写明修饰符,在同一包内可见(类,接口,变量,方法可用)
    
        接口中变量隐式声明为public static final,方法隐式声明为public
    

    public:

        对所有的类可见(类、接口、方法、变量可用)
    
        java 中main方法一定为共有的
    

    protected:

        同一个包中的所有子类和其他类都可以访问(变量、方法可用,不能修饰类(外部类)和接口(及接口的变量和方法))
    
        不同包中的子类可见(变量、方法可用,不能修饰类(外部类)和接口(及接口的变量和方法)),其他类不能访问。
    
        不同包中的子类不能使用父类的实例对象访问其父类的protect属性的变量和方法。即:superClass.protectParameter不可见。只能subClass.protectParameter访问。
    
        避免不相关的类访问。
    

    private:

        最严格访问级别,为了隐藏类实现的细节,保护类的数据,外部类和接口不能声明为private。
    
        只能在同一个类内可见(变量、方法可用,不能修饰外部类和接口(及接口的变量和方法))
    

    访问修饰符在继承中的规则:子类权限必须大于父类,具体如下:

        1.父类public子类必为public.
        2.父类protected子类必须protected/public.
        3. 父类private子类无法继承.
    

    非访问修饰符:

    abstract:

        创建抽象类和抽象方法
    
        一个类不能同时被abstract和final修饰,一个类只要包含抽象方法,就一定要声明为抽象类。抽象类可以包含抽象和非抽象的方法,也可以不包含抽象方法。
    
        抽象方法不能被声明为static 和final,子类必须实现父类的抽象方法,除非子类也是抽象类
    

    static:

        可以直接通过类名访问static修饰的变量和方法,如StaticClass.variableName /StaticClass.methodName().
    
        static 变量:也叫类变量,属于类所有,独立于类的实例对象(实例对象可以有多个,类变量只有一份),
        只能修饰成员变量,不可修饰局部变量。
    
        static 方法:类方法,独立于类的实例对象,方法内不能包含非静态的成员变量。
    

    final:

        final修饰的类不能被继承,方法不能被子类重写,变量为常量不可修改。
    
        final修饰的变量必须显示初始化并且只能赋值一次。
    
        final变量不可修改,是指的其引用不可修改,引用变量的值改变,fianl变量包含的数据是可以改变的。:
        final Person p = new Person(24,name);
        p.setAge(25);
    
        final一般结合static创建常量。static final String CONSTANT_STRING = "I'M CONSTANT STRING"
    
        final修饰的方法不可被子类修改,可以防止该方法被改动
    

    synchronized:

        说明被修饰的方法在同一个时间只能被一个线程访问,synchronized修饰符可以用应于 所有访问修饰符(public、default、private、protected)
    

    transient:

        序列化对象时,JVM会跳过transient修饰的变量。public transient String str = "I'll be ignored";
    

    volatile

        volatile修饰的成员变量在每次被线程访问时,都需要从共享内存中重新读取该成员变量的值,
        当此成员变量发生变化时,会强制线程将变化值写回共享内存,因此,在任何时刻,多个线程中此成员变量的值都相同。
    

    native

        在JAVA中调用非JAVA语言写的方法(如C、C++) :native void nativeMethod(.....);
        native 不可以和abstract修饰符一起使用。(native虽然在JAVA代码没有方法体,但本地方法是有实现的)
        使用native主要是为了和java环境和操作系统等交互,本地方法在JAVA加载是,放在DLL文件中,方法native方法调用前,本地方法会被加载。
    

    Thanks:

    https://blog.csdn.net/u011240877/article/details/54604146

    展开全文
  • //新建一个类库项目,增加一个GetSum方法。 using System; namespace ClassLibrary1 { public class Class1 { public Class1() { } public int GetSum(int x, int y) ...

    展开全文
  • 原创文章,转载请注明出处。本节中,我们来看看Spring针对反射提供的工具类:ReflectionUtils。反射在容器中使用是非常频繁的了,ReflectionUtils中也提供了想当多的有用的方法,一起来看看。在ReflectionUtils中...

    原创文章,转载请注明出处。

    本节中,我们来看看Spring针对反射提供的工具类:ReflectionUtils。反射在容器中使用是非常频繁的了,ReflectionUtils中也提供了想当多的有用的方法,一起来看看。

    在ReflectionUtils中提供了一些专门用于处理在反射中异常相关的方法,这些方法一般在Spring框架内部使用,当然,出于规范考虑,我们在开发中涉及到反射的异常,也可以使用这些方法。我们按照这些方法的调用链来看代码:


    void handleReflectionException(Exception ex)
    处理反射中的异常,我们直接看代码:

    public static void handleReflectionException(Exception ex) {
        if (ex instanceof NoSuchMethodException) {
            throw new IllegalStateException("Method not found: " + ex.getMessage());
        }
        if (ex instanceof IllegalAccessException) {
            throw new IllegalStateException("Could not access method: " + ex.getMessage());
        }
        if (ex instanceof InvocationTargetException) {
            handleInvocationTargetException((InvocationTargetException) ex);
        }
        if (ex instanceof RuntimeException) {
            throw (RuntimeException) ex;
        }
        throw new UndeclaredThrowableException(ex);
    }
    

    可以看到,代码把NoSuchMethodException,IllegalAccessException,InvocationTargetException等反射中的检查异常(Exception)直接包装为对应的运行时异常(RuntimeException);这里我们可以先看一下反射中的异常:

    image.png
    可以看到,反射中的异常,都继承了ReflectiveOperationException(反射操作异常),而该异常继承了Exception;旗下再是ClassNotFoundException等具体的反射操作异常;

    在方法中,如果遇到的是InvocationTargetException异常,交给handleInvocationTargetException方法处理:

    public static void rethrowRuntimeException(Throwable ex) {
        if (ex instanceof RuntimeException) {
            throw (RuntimeException) ex;
        }
        if (ex instanceof Error) {
            throw (Error) ex;
        }
        throw new UndeclaredThrowableException(ex);
    }
    

    可以看到,该方法只是判断了运行时异常和Error;并原样抛出;怎么理解这个方法的调用?原因很简单,InvocationTargetException是在method.invoke的时候抛出的,方法在执行的过程中,方法本身的执行可能抛出RuntimeException或者Error,其余方法本身抛出的Exception异常直接包装为UndeclaredThrowableException(RuntimeException)处理;

    统一下来,可以这样理解,除了在反射执行过程中遇到的Error,其余所有的Exception,都被统一转成了RuntimeException;


    接下来进入正常方法
    Field findField(Class<?> clazz, String name, Class<?> type)
    根据类型,字段名称和字段类型查询一个字段;该方法会遍历的向父类查询字段,查询到的是所有字段;我们可以简单看一下实现:

    public static Field findField(Class<?> clazz, String name, Class<?> type) {
        Class<?> searchType = clazz;
        while (Object.class != searchType && searchType != null) {
            Field[] fields = getDeclaredFields(searchType);
            for (Field field : fields) {
                if ((name == null || name.equals(field.getName())) &&
                        (type == null || type.equals(field.getType()))) {
                    return field;
                }
            }
            searchType = searchType.getSuperclass();
        }
        return null;
    }
    

    代码实现比较简单,向上查询字段,直到Object类型;注意,其中调用了一句代码:

    Field[] fields = getDeclaredFields(searchType);
    

    我们来看看该代码实现:

    private static Field[] getDeclaredFields(Class<?> clazz) {
        Field[] result = declaredFieldsCache.get(clazz);
        if (result == null) {
            result = clazz.getDeclaredFields();
            declaredFieldsCache.put(clazz, (result.length == 0 ? NO_FIELDS : result));
        }
        return result;
    }
    

    可以看到,实际上,在该工具类中,对类型和字段做了缓存,保存到了declaredFieldsCache中,来看看这个cache的声明:

    private static final Map<Class<?>, Field[]> declaredFieldsCache =
            new ConcurrentReferenceHashMap<Class<?>, Field[]>(256);
    

    因为是工具类,被声明为ConcurrentReferenceHashMap也是能够理解;
    这种缓存机制也存在于方法的查询;

    该方法还有一个简单的版本:
    Field findField(Class<?> clazz, String name)

    void setField(Field field, Object target, Object value)
    在指定对象(target)中给指定字段(field)设置指定值(value);

    Object getField(Field field, Object target) 
    在指定对象(target)上得到指定字段(field)的值;
    以上两个方法的实现都非常简单,分别调用了field.set和field.get方法;并处理了对应的异常;选一个实现看看:

    public static void setField(Field field, Object target, Object value) {
        try {
            field.set(target, value);
        }
        catch (IllegalAccessException ex) {
            handleReflectionException(ex);
            throw new IllegalStateException(
                    "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage());
        }
    }
    

    使用了handleReflectionException方法来统一处理异常;


    Method findMethod(Class<?> clazz, String name, Class<?>… paramTypes) 
    在类型clazz上,查询name方法,参数类型列表为paramTypes;

    public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
        Class<?> searchType = clazz;
        while (searchType != null) {
            Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
            for (Method method : methods) {
                if (name.equals(method.getName()) &&
                        (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
                    return method;
                }
            }
            searchType = searchType.getSuperclass();
        }
        return null;
    }
    

    可以看到,该仍然可以向上递归查询方法,并且查询到的是所有方法。

    该方法也有一个简单的版本:
    Method findMethod(Class<?> clazz, String name)

    Object invokeMethod(Method method, Object target, Object… args)
    在指定对象(target)上,使用指定参数(args),执行方法(method);
    该方法的实现也非常简单:

    public static Object invokeMethod(Method method, Object target, Object... args) {
        try {
            return method.invoke(target, args);
        }
        catch (Exception ex) {
            handleReflectionException(ex);
        }
        throw new IllegalStateException("Should never get here");
    }
    

    可以看到,就只是调用了method.invoke方法,并统一处理了调用异常;
    该方法也有一个简单版本:
    Object invokeMethod(Method method, Object target)


    boolean declaresException(Method method, Class<?> exceptionType)
    判断一个方法上是否声明了指定类型的异常;

    boolean isPublicStaticFinal(Field field)
    判断字段是否是public static final的;

    boolean isEqualsMethod(Method method)
    判断方法是否是equals方法;

    boolean isHashCodeMethod(Method method)
    判断方法是否是hashcode方法;

    boolean isToStringMethod(Method method)
    判断方法是否是toString方法;

    可能有童鞋记得,在AopUtils中也有这几个isXXX方法,是的,其实AopUtils中的isXXX方法就是调用的ReflectionUtils的这几个方法的;

    boolean isObjectMethod(Method method)
    判断方法是否是Object类上的方法;


    void makeAccessible(Field field)
    将一个字段设置为可读写,主要针对private字段;

    void makeAccessible(Method method)
    将一个方法设置为可调用,主要针对private方法;

    void makeAccessible(Constructor<?> ctor)
    将一个构造器设置为可调用,主要针对private构造器;

    void doWithLocalMethods(Class<?> clazz, MethodCallback mc)
    针对指定类型上的所有方法,依次调用MethodCallback回调;
    首先来看看MethodCallback接口声明:

    public interface MethodCallback {
    
        /**
         * 使用指定方法完成一些操作.
         */
        void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;
    }
    

    其实就是一个正常的回调接口;来看看doWithLocalMethods实现:

    public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
        Method[] methods = getDeclaredMethods(clazz);
        for (Method method : methods) {
            try {
                mc.doWith(method);
            }catch (IllegalAccessException ex) {
                throw new IllegalStateException("...");
            }
        }
    }
    

    其实实现很简单,就是得到类上的所有方法,然后执行回调接口;这个方法在Spring针对bean的方法上的标签处理时大量使用,比如@Init@Resource@Autowire等标签的预处理;

    该方法有一个增强版:
    void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf)
    该版本提供了一个方法匹配(过滤器)MethodFilter;
    我们来看看MethodFilter的接口声明:

    public interface MethodFilter {
    
        /**
         * 检查一个指定的方法是否匹配规则
         */
        boolean matches(Method method);
    }
    

    该接口就声明了一个匹配方法,用于匹配规则;
    再返回来看看doWithMethods方法的实现:

    public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) {
        // Keep backing up the inheritance hierarchy.
        Method[] methods = getDeclaredMethods(clazz);
        for (Method method : methods) {
            if (mf != null && !mf.matches(method)) {
                continue;
            }
            try {
                mc.doWith(method);
            }catch (IllegalAccessException ex) {
                throw new IllegalStateException("...");
            }
        }
        if (clazz.getSuperclass() != null) {
            doWithMethods(clazz.getSuperclass(), mc, mf);
        }else if (clazz.isInterface()) {
            for (Class<?> superIfc : clazz.getInterfaces()) {
                doWithMethods(superIfc, mc, mf);
            }
        }
    }
    

    该方法实现就很明确了,首先得到类上所有方法,针对每一个方法,调用MethodFilter实现匹配检查,如果匹配上,调用MethodCallback回调方法。该方法会递归向上查询所有父类和实现的接口上的所有方法并处理;

    void doWithLocalFields(Class<?> clazz, FieldCallback fc)
    那很明显,该方法就是针对所有的字段,执行的对应的回调了,这里的FieldCallback就类似于前面的MethodCallback:

    public interface FieldCallback {
    
        /**
         * 给指定的字段执行操作;
         */
        void doWith(Field field) throws IllegalArgumentException, IllegalAccessException;
    }
    

    该方法的实现就类似于doWithLocalMethods的实现了:

    public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
        for (Field field : getDeclaredFields(clazz)) {
            try {
                fc.doWith(field);
            }catch (IllegalAccessException ex) {
                throw new IllegalStateException("...");
            }
        }
    }
    

    得到类上所有的字段,并执行回调;同理,该方法在Spring中主要用于预处理字段上的@Autowire或者@Resource标签;

    void doWithFields(Class<?> clazz, FieldCallback fc, FieldFilter ff)
    和doWithMethods的加强版相同,针对字段,也提供了一个拥有字段匹配(过滤)的功能方法;我们就只简单看下FieldFilter实现即可:

    public interface FieldFilter {
    
        /**
         * 检查给定字段是否匹配;
         */
        boolean matches(Field field);
    }
    

    小结

    总的来说,ReflectionUtils提供的功能还算完整,其实想要实现这样的一个工具类,也不是什么难事,更多的建议大家多看看Spring的实现,还是有不少收获。当然,这里我们看的是Spring4.X的代码,相信在Spring5中完全使用Java8的代码,会更优雅。


    展开全文
  • Reflection

    2019-04-03 15:25:00
    1.反射 1.1 System.Type类 1.2 使用System.Object.GetType()得到Type的引用 1.3 使用typeof()得到Type引用 1.4 使用System.Type.GetType()得到Type引用 2.构建自定义的元数据查看器 ...2.4 显...
  • 反射(Reflection

    2020-10-09 09:56:22
    反射(Reflection) java是一种静态语言 反射是java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。 Class c = Class.for...
  • 什么是反射(Reflection )? 主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。 什么是Java反射? Java反射指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定...
  • Java Reflection

    千次阅读 2004-11-20 15:46:00
    ReflectionJava 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。 Java 的这一能力在...
  • java reflection

    2007-09-30 10:00:13
    Java Reflection(反射)是一种在运行期间查看Java对象内部情况的技术,包括Java对象的变量、支持的方法、实现的接口、扩展的类——基本上在编译时你能想知道的关于对象的任何东西。 Reflection API位于java.lang....
  • Java Reflection

    2014-02-22 14:34:26
    “反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为。”这个概念常常会和内省(Introspection)混淆,以下是这两个术语在Wikipedia中的解释: 内省用于在运行时检测某个对象的类型和其包含的...
  • Java Reflection教程

    2017-02-10 14:47:10
    Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取变量的值。 Java反射机制功能...
  • 转载需注明出处:java反射机制(1)- 知识点总结Java Reflection API操作1 什么是反射机制  什么是反射机制?简单点说就是程序在运行时能够获取自身的信息。在java中,只要给定类的全名,就可以通过反射机制来获取...
  • Java Reflection 相关及示例

    千次阅读 2014-12-26 21:22:27
    摘要: 记录了一些关于Java反射的知识、最重要的是对Class这个类的理解、熟悉反射相关的几个类、通过代码示例加深对Java底层的一些东西的理解。
  • 当年《Java Reflection In Action》的出版商网址无法访问,因此无法下载源代码。许多告诉我如何翻越长城,那时他才上高一。 下载了,也就完成任务了,也没看。一晃4,5年过去了。 今天给一个例子。 import c2c.*...
  • Java Reflection API学习

    千次阅读 2005-03-16 16:58:00
    一、Java Reflection API的测试package tigers;import java.lang.reflect.*;import java.util.*;import java.sql.*;import java.io.*;public class Tiger7 { private final String name; private transient int id...
  • Java Reflection Tutorial

    2016-04-08 14:34:00
    为什么80%的码农都做不了架构师?>>> ...
  • Java Reflection in Action

    2016-11-11 14:41:55
    Java Reflection in Action
  • Java Reflection API简介

    千次阅读 2008-05-15 16:16:00
    本章首先介绍了Java Reflection API的用法,然后介绍了一个远程方法调用的例子,在这个例子中客户端能够远程调用服务器端的一个对象的方法。服务器端采用了反射机制提供的动态调用方法的功能,而客户端则采用了反射...
  • Java Reflection in Action 英文书 pdf和文中源代码
  • 我常常在一些文章以及论坛中读到说Java泛型信息在编译期被擦除(erased)所以你无法在运行期获得有关泛型的信息。其实这种说法并不完全正确的,在一些情况下是...package reflection.generic; import java.util.Arra
  • Link:...Java Reflection provides ability to inspect and modify the runtime behavior of application. Reflection in Java i...
  • Java Reflection (JAVA反射)

    2005-01-24 10:30:00
    CSDN - 文档中心 - Java 阅读:4302 评论: 1 参与评论 标题 Java Reflection (JAVA反射) 选择自 nootfly 的 Blog 关键字 Java Reflection (JAVA反射) 出处 Java Reflection (JAVA反射) Reflection 是 ...
  • Java之六:Java Reflection

    2014-07-19 14:19:05
    本博文主要讲诉Java Reflection的定义、相关类以及主要用途 Java Reflection: 我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,是在运行状态中,对于任意一个类,都能够知道这个类的...
  • Using Java Reflection

    2011-08-16 14:09:45
    http://java.sun.com/developer/technicalArticles/ALT/Reflection/ By Glen McCluskey January 1998 Reflection is a feature in the Java
  • Java中反射(Reflection)的应用

    千次阅读 2016-09-16 17:37:20
    本片博客为大家讲述的是Java编程中的高级应用--->反射机制的应用,讲解了使用反射机制所带来的方便以及反射应用的重要性。同时,为大家介绍了可以通过反射机制获得的查询对象以及具体方法。
  • java中使用Reflection API获取构造方法或普通方法的参数名是一件比较麻烦的事情,而Java 8提供java.lang.reflect.Parameter类,它将提供有关参数名称及其修饰符的信息。在Java 8之前,我们不能直接获取方法或构造...
  • Java Reflection - Fields

    2015-05-31 11:51:29
    http://tutorials.jenkov.com/java-reflection/fields.html ...Using Java Reflection you can inspect the fields (member variables) of classes and get / set them at runtime. This is done via the Java
  • from:http://tutorials.jenkov.com/java-reflection/index.html   Despite the common belief it is actually possible to access private fields and ... of other classes via Java Reflection. It is n...
  • 利用Java的反射机制你可以检查一个类的构造方法,并且可以在运行期创建一个对象。这些功能都是通过java.lang.reflect.Constructor这个类实现的。本节将深入的阐述Java Constructor对象。获取Constructor对象我们可以...
  • java的运行环境中,对于一个人任意的类,如果要获取这个类的所有属性和方法(公有的和私有的),并且能够调用这个类的任意方法,那么可以借助Java语言的反射(Reflection)机制来动态获取类的信息。  Java反射...

空空如也

1 2 3 4 5 ... 20
收藏数 106,527
精华内容 42,610
关键字:

reflection