定义

Java 反射机制在程序运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种 动态的获取信息 以及 动态调用对象的方法 的功能称为 java 的反射机制。

  1. 反射允许我们可以在程序运行时加载和使用编译期间完全未知的.class文件,就是在运行的时候才知道名称的class文件,然后才知道他的完整构造,生成对象实体,调用方法等等
1
2
3
4
5
6
7
8
9
10
11
12
13
// 根据路径找到类
Class clz = Class.forName("com.reflect.Apple");
// 得到该类的方法
Method method = clz.getMethod("setPrice", int.class);
// 得到该类的构造器
Constructor constructor = clz.getConstructor();
// 得到具体对象
Object object = constructor.newInstance();
// 在该对象上执行该方法
method.invoke(object, 4);
//等价于:
Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);

优缺点

  1. 可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中。比如,一个大型的软件,当这个程序编译后,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

步骤

注意一定要在try/catch中执行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
Class clz = Class.forName("com.reflect.api.Apple");

Constructor appleConstructor = clz.getConstructor();

Object appleObj = appleConstructor.newInstance();

Method setPriceMethod = clz.getMethod("setPrice", int.class);

setPriceMethod.invoke(appleObj, 14);

Method getPriceMethod = clz.getMethod("getPrice");

System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
  1. 获取类的Class对象实例

  2. 根据Class对象获取Constructor对象

  3. 根据newInstance方法获取反射的对象

  4. 如果要调用某一个方法,就需要:

  5. 获取方法的Method

  6. 利用invoke调用方法(传入反射得到的对象Object)

常用API

  • 获取Class对象
1
2
3
4
5
6
7
Class clz = Class.forName("java.lang.String");

Class clz = String.class;

String str = new String("Hello");

Class clz = str.getClass();
  • 创建Object对象
1
2
3
4
5
Apple apple = (Apple)clz.newInstance();

//利用constructor可以选择特定构造方法
Constructor constructor = clz.getConstructor(String.class, int.class);
Apple apple = (Apple)constructor.newInstance("红富士", 15);
  • 获取类属性
1
2
3
4
5
Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}//包括私有属性,getFields返回非私有属性
  • 获取类的所有方法,用methods替换上面的Fields,获取单个方法的时候使用getMethod或者getDeclaredMethod,并且在后面括号加上方法名,传入参数的类型.class,

注意:如果是私有方法,需要调用setAccessible(true),私有属性同理

举例:

1
<bean class="com.reflect.Apple"></bean>

Spring 便会在启动的时候利用反射去加载对应的 Apple 类。而当 Apple 类不存在或发生启发异常时,异常堆栈便会将异常指向调用的 invoke 方法。

Invoke()方法解析

注意看源码

  1. 代理模式,三个MethodAccessor实现类,Delegating,Native以及无前缀的MechodAccessorImpl

  2. 代码先判断是否存在对应的 MethodAccessor 对象,如果存在那么就复用之前的 MethodAccessor 对象

  3. 如果不存在就调用acquireMethodAccessor,调用reflectionFactory对象 的 newMethodAccessor 方法生成一个 MethodAccessor 对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException,
    InvocationTargetException
    {
    if (!override) {
    if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
    Class<?> caller = Reflection.getCallerClass();
    checkAccess(caller, clazz, obj, modifiers);
    }
    }
    MethodAccessor ma = methodAccessor; // read volatile
    if (ma == null) {
    ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
    }

    private MethodAccessor acquireMethodAccessor() {
    // First check to see if one has been created yet, and take it
    // if so
    MethodAccessor tmp = null;
    if (root != null) tmp = root.getMethodAccessor();
    if (tmp != null) {
    methodAccessor = tmp;
    } else {
    // Otherwise fabricate one and propagate it up to the root
    tmp = reflectionFactory.newMethodAccessor(this);
    setMethodAccessor(tmp);
    }

    return tmp;
    }
  4. 先返回的是reflectionFactory生成的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    public MethodAccessor newMethodAccessor(Method var1) {
    checkInitted();
    if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
    return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
    }
    else {
    NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
    DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
    var2.setParent(var3);
    return var3;
    }
    }
  5. 最终是DelegatingMethodAccessorImpl调用invoke方法,然后再进入其代理的NativeMathodAccessorImpl的Invoke方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public MethodAccessor newMethodAccessor(Method var1) {
    checkInitted();
    if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
    return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
    }
    else {
    NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
    DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
    var2.setParent(var3);
    // 返回的DelegatingMethodAccessor
    return var3;
    }
    }
  6. 进入 DelegatingMethodAccessorImpl 类的 invoke 方法后,这里调用了 delegate 属性的 invoke 方法,它又有两个实现类,分别是:DelegatingMethodAccessorImpl 和 NativeMethodAccessorImpl

    这里的 delegate 其实是一个 NativeMethodAccessorImpl 对象,所以这里会进入 NativeMethodAccessorImpl 的 invoke 方法。

    1
    2
    3
    4
    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
    return this.delegate.invoke(var1, var2);
    }

  7. 超过该阀值,那么就会生成另一个MethodAccessor 对象,并将原来 DelegatingMethodAccessorImpl 对象中的 delegate 属性指向最新的 MethodAccessorImpl对象。

    1
    2
    3
    4
    5
    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
    if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
    MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
    this.parent.setDelegate(var3);
    }

    Native 版本一开始启动快,但是随着运行时间边长,速度变慢。Java 版本一开始加载慢,但是随着运行时间边长,速度变快。正是因为两种存在这些问题,所以第一次加载的时候我们会发现使用的是 NativeMethodAccessorImpl 的实现,而当反射调用次数超过 15 次之后,则使用 MethodAccessorGenerator 生成的 MethodAccessorImpl 对象去实现反射。

总体流程

图源博客园@陈树义

绕过编译检查

在编译的时候,泛型会限制集合内元素类型保持一致,但是在运行期泛型不起作用,那么可以通过反射给一个存放String类型的list,在运行的时候添加int

References

https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html