从JVM的角度来深入了解Java反射

TIP

本文收录于《www.yuque.com/javahome/ja…》 ,更多深度好文请前往《www.yuque.com/javahome/ja…》 。

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。Java反射机制在框架设计中极为广泛,需要深入理解。本文综合多篇文章后,总结了Java 反射的相关知识,希望可以提升你对Java中反射的认知效率。

image.png

反射基础

在《Thinking in Java》一书的第十四章中有提到,其作用是在运行时识别一个对象的类型和类的信息。主要有两种方式:一种是“传统的”RTTI((Run-Time Type Identification)运行时类型识别),它假定我们在编译时已经知道了所有的类型;另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。

Class类

Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。 Class类的实例表示java应用运行时的类(class ans enum)或接口(interface and annotation)(每个java类运行时都在JVM里表现为一个class对象,可通过类名.class、类型.getClass()、Class.forName("类名")等方法获取class对象)。数组同样也被映射为为class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本类型boolean,byte,char,short,int,long,float,double和关键字void同样表现为 class 对象。

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    private static final int ANNOTATION = 0x00002000;
    private static final int ENUM = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();

    static {
        registerNatives();
    }

    /*
     * Constructor. Only the Java Virtual Machine creates Class
     * objects.
     */
    private Class() {
    }

    // ......
}
复制代码

到此,我们能得出几个信息:

  • Class类也是类的一种,与class关键字是不一样。
  • 手动编写的类被编译后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class的文件中(字节码文件)
  • 每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。
  • Class类只存私有构造函数,因此对应Class对象只能有JVM创建和加载
  • Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要(对于反射稍后分析)。

类加载

对于类加载,可以参考一下文章

类的加载有三个阶段:

  • 加载
  • 连接
  • 初始化

连接又分为三个阶段:

  • 验证
  • 准备
  • 解析

image.png

扫描二维码关注公众号,回复: 13687392 查看本文章

image.png

反射的使用

通过本节我们能够收获:如何通过反射获取Class类对象以及类中的成员变量、方法、构造方法等。

在Java中,Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。我们看一下java.lang.reflect类库有什么东西:

image.png

在反射包中,我们常用的类主要有Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)、Method表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private),下面将对这几个重要类进行分别说明。

Class类对象的获取

获取class对象的方式的主要有三种:

  • 根据类名:类名.class
  • 根据对象:对象.getClass()
  • 根据全限定类名:Class.forName(全限定类名)
package com.example;

/**
 * @author xjh
 * created on 2021-10-06 5:31 下午
 */
public class Demo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<Demo> clazz1 = Demo.class;

        Demo demo = new Demo();
        Class<? extends Demo> clazz2 = demo.getClass();

        Class<?> clazz3 = Class.forName("com.example.Demo");
    }
}
复制代码

Constructor类

Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。

获取Constructor对象是通过Class类中的方法获取的,Class类与Constructor相关的主要方法如下:

Constructor getConstructor(Class<?>… parameterTypes):返回指定参数类型、具有public访问权限的构造函数对象。

Constructor<?>[] getConstructors():返回所有具有public访问权限的构造函数的Constructor对象数组。

Constructor getDeclaredConstructor(Class<?>… parameterTypes):返回指定参数类型、所有声明的(包括private)构造函数对象。

Constructor<?>[] getDeclaredConstructor():返回所有声明的(包括private)构造函数对象。

接下来请看一个示例:

public class ConstructionTest implements Serializable {
    public static void main(String[] args) throws Exception {

        Class<?> clazz = null;
        // 获取Class对象的引用
        clazz = Class.forName("com.example.User");

        System.out.println("--------------------------------------------");
        // 第一种方法,实例化默认构造方法,User必须无参构造函数,否则将抛异常
        User user = (User) clazz.newInstance();
        user.setAge(20);
        user.setName("hellojava");
        System.out.println("user:" + user.toString());
        System.out.println("--------------------------------------------");

        System.out.println("--------------------------------------------");
        // 获取带String参数的public构造函数,使用它进行初始化对象user1
        Constructor cs1 = clazz.getConstructor(String.class);
        User user1 = (User) cs1.newInstance("hellojava1");
        user1.setAge(22);
        System.out.println("user1:" + user1.toString());
        System.out.println("--------------------------------------------");

        System.out.println("--------------------------------------------");
        // 取得指定带int和String参数构造函数,该方法是私有构造private
        Constructor cs2 = clazz.getDeclaredConstructor(int.class, String.class);
        // 由于是private必须设置可访问
        cs2.setAccessible(true);
        // 创建user对象
        User user2 = (User) cs2.newInstance(25, "hellojava2");
        System.out.println("user2:" + user2.toString());
        System.out.println("--------------------------------------------");

        System.out.println("--------------------------------------------");
        // 获取所有构造包含private
        Constructor<?> cons[] = clazz.getDeclaredConstructors();
        // 查看每个构造方法需要的参数
        for (int i = 0; i < cons.length; i++) {
            // 获取构造函数参数类型
            Class<?> clazzs[] = cons[i].getParameterTypes();
            System.out.println("构造函数["+i+"]:"+cons[i].toString() );
            System.out.print("参数类型["+i+"]:(");
            for (int j = 0; j < clazzs.length; j++) {
                if (j == clazzs.length - 1)
                    System.out.print(clazzs[j].getName());
                else
                    System.out.print(clazzs[j].getName() + ",");
            }
            System.out.println(")");
        }
        System.out.println("--------------------------------------------");
    }
}

class User {
    private int age;
    private String name;
    public User() {
        super();
    }
    public User(String name) {
        super();
        this.name = name;
    }

    /**
     * 私有构造
     * @param age
     * @param name
     */
    private User(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
复制代码

输出结果:

--------------------------------------------
user:User{age=20, name='hellojava'}
--------------------------------------------
--------------------------------------------
user1:User{age=22, name='hellojava1'}
--------------------------------------------
--------------------------------------------
user2:User{age=25, name='hellojava2'}
--------------------------------------------
--------------------------------------------
构造函数[0]:public com.example.User()
参数类型[0]:()
构造函数[1]:private com.example.User(int,java.lang.String)
参数类型[1]:(int,java.lang.String)
构造函数[2]:public com.example.User(java.lang.String)
参数类型[2]:(java.lang.String)
--------------------------------------------
复制代码

Constructor类本身的一些常用方法有: Class getDeclaringClass():返回 Class 对象,该对象表示声明由此 Constructor 对象表示的构造方法的类,其实就是返回真实类型(不包含参数)。 String getName():以字符串形式返回此构造方法的名称。 String toGenericString():返回描述此 Constructor 的字符串,其中包括类型参数。 Class<?>[] getParameterTypes():按照声明顺序返回一组 Class 对象,即返回Constructor 对象所表示构造方法的形参类型。 Type[] getGenericParameterTypes():按照声明顺序返回一组 Type 对象,返回的就是 Constructor对象构造函数的形参类型。 T newInstance(Object… initargs):使用此 Constructor对象表示的构造函数来创建新实例。

Field类

Field 提供有关类或接口的属性字段的信息,以及对它的动态访问权限。

同样的道理,我们可以通过Class类的提供的方法来获取代表字段信息的Field对象,Class类与Field对象相关方法如下:

Field[] getField():获取修饰符为public的字段,包含继承字段。 Field getField(String name):获取指定name名称、具有public修饰的字段,包含继承字段。 Field[] getDeclaredField():获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段。 Field getDeclaredField(String name):获取指定name名称的(包含private修饰的)字段,不包括继承的字段。

下面请看一个示例:

public class ReflectField {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<?> clazz = Class.forName("reflect.Student");
        // 获取指定字段名称的Field类,注意字段修饰符必须为public而且存在该字段,
        // 否则抛NoSuchFieldException
        Field field = clazz.getField("age");
        System.out.println("field:" + field);

        // 获取所有修饰符为public的字段,包含父类字段,注意修饰符为public才会获取
        Field fields[] = clazz.getFields();
        for (Field f : fields) {
            System.out.println("f:" + f.getDeclaringClass());
        }

        System.out.println("================getDeclaredFields====================");
        // 获取当前类所字段(包含private字段),注意不包含父类的字段
        Field fields2[] = clazz.getDeclaredFields();
        for (Field f : fields2) {
            System.out.println("f2:" + f.getDeclaringClass());
        }
        // 获取指定字段名称的Field类,可以是任意修饰符的自动,注意不包含父类的字段
        Field field2 = clazz.getDeclaredField("desc");
        System.out.println("field2:" + field2);

        /**
             输出结果: 
             field:public int reflect.Person.age
             f:public java.lang.String reflect.Student.desc
             f:public int reflect.Person.age
             f:public java.lang.String reflect.Person.name

             ================getDeclaredFields====================
             f2:public java.lang.String reflect.Student.desc
             f2:private int reflect.Student.score
             field2:public java.lang.String reflect.Student.desc
         */

        // --------------------

        // 获取Class对象引用
        Class<?> clazz = Class.forName("reflect.Student");

        Student st = (Student) clazz.newInstance();
        // 获取父类public字段并赋值
        Field ageField = clazz.getField("age");
        ageField.set(st, 18);
        Field nameField = clazz.getField("name");
        nameField.set(st, "Lily");

        // 只获取当前类的字段,不获取父类的字段
        Field descField = clazz.getDeclaredField("desc");
        descField.set(st, "I am student");
        Field scoreField = clazz.getDeclaredField("score");
        // 设置可访问,score是private的
        scoreField.setAccessible(true);
        scoreField.set(st, 88);
        System.out.println(st.toString());
        // 输出结果:Student{age=18, name='Lily ,desc='I am student', score=88} 

        // 获取字段值
        System.out.println(scoreField.get(st));
        // 88
    }
}

class Person{
    public int age;
    public String name;
    //省略set和get方法
}

class Student extends Person{
    public String desc;
    private int score;
    //省略set和get方法
}
复制代码

接下来看看Field类提供的一些常见API: Object get(Object obj):返回指定对象上此 Field 表示的字段的值。 void setAccessible(boolean flag):将此对象的 accessible 标志设置为指示的布尔值,即设置其可访问性。 void set(Object obj, Object value):将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 Class getType():返回一个 Class 对象,它标识了此Field 对象所表示字段的声明类型。 String getName():返回此 Field 对象表示的字段的名称。 String toGenericString():返回一个描述此 Field(包括其一般类型)的字符串。boolean isEnumConstant():如果此字段表示枚举类型的元素则返回 true;否则返回 false。 Class getDeclaringClass():返回表示类或接口的 Class 对象,该类或接口声明由此 Field 对象表示的字段。

Method类

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。

下面是Class类获取Method对象相关的方法:

Method getDeclaredMethod(String name, Class… parameterTypes):返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 Method[] getDeclaredMethod():返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 Method getMethod(String name, Class… parameterTypes):返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 Method[] getMethods():返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

同样的,我们看一个示例:

import java.lang.reflect.Method;

public class ReflectMethod  {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {

        Class clazz = Class.forName("reflect.Circle");

        // 根据参数获取public的Method,包含继承自父类的方法
        Method method = clazz.getMethod("draw", int.class, String.class);

        System.out.println("method:" + method);

        // 获取所有public的方法:
        Method[] methods = clazz.getMethods();
        for (Method m : methods){
            System.out.println("m::" + m);
        }

        System.out.println("=========================================");

        // 获取当前类的方法包含private,该方法无法获取继承自父类的method
        Method method1 = clazz.getDeclaredMethod("drawCircle");
        System.out.println("method1::" + method1);
        // 获取当前类的所有方法包含private,该方法无法获取继承自父类的method
        Method[] methods1 = clazz.getDeclaredMethods();
        for (Method m : methods1){
            System.out.println("m1::" + m);
        }
    }
}

class Shape {
    public void draw(){
        System.out.println("draw");
    }

    public void draw(int count , String name){
        System.out.println("draw "+ name +",count="+count);
    }

}
class Circle extends Shape{

    private void drawCircle(){
        System.out.println("drawCircle");
    }
    public int getAllCount(){
        return 100;
    }
}
复制代码

输出结果:

method:public void reflect.Shape.draw(int,java.lang.String)

m::public int reflect.Circle.getAllCount()
m::public void reflect.Shape.draw()
m::public void reflect.Shape.draw(int,java.lang.String)
m::public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
m::public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
m::public final void java.lang.Object.wait() throws java.lang.InterruptedException
m::public boolean java.lang.Object.equals(java.lang.Object)
m::public java.lang.String java.lang.Object.toString()
m::public native int java.lang.Object.hashCode()
m::public final native java.lang.Class java.lang.Object.getClass()
m::public final native void java.lang.Object.notify()
m::public final native void java.lang.Object.notifyAll()

=========================================
method1::private void reflect.Circle.drawCircle()

m1::public int reflect.Circle.getAllCount()
m1::private void reflect.Circle.drawCircle()
复制代码

在通过getMethods方法获取Method对象时,会把父类的方法也获取到,如上的输出结果,把Object类的方法都打印出来了。而getDeclaredMethod/getDeclaredMethods方法都只能获取当前类的方法。我们在使用时根据情况选择即可。

下面将演示通过Method对象调用指定类的方法:

Class clazz = Class.forName("reflect.Circle");
// 创建对象
Circle circle = (Circle) clazz.newInstance();

// 获取指定参数的方法对象Method
Method method = clazz.getMethod("draw", int.class, String.class);

// 通过Method对象的invoke(Object obj, Object... args)方法调用
method.invoke(circle, 15, "圈圈");

// 对私有无参方法的操作
Method method1 = clazz.getDeclaredMethod("drawCircle");
// 修改私有方法的访问标识
method1.setAccessible(true);
method1.invoke(circle);

//对有返回值得方法操作
Method method2 = clazz.getDeclaredMethod("getAllCount");
Integer count = (Integer) method2.invoke(circle);
System.out.println("count:" + count);
复制代码

输出结果:

draw 圈圈,count=15
drawCircle
count:100
复制代码

下面来看一下Method提供的一些常用API: Object invoke(Object obj, Object… args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 Class getReturnType():返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型,即方法的返回类型 Type getGenericReturnType():返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象,也是方法的返回类型。 Class[] getParameterTypes():按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。即返回方法的参数类型组成的数组 Type[] getGenericParameterTypes():按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的,也是返回方法的参数类型 String getName():以 String 形式返回此 Method 对象表示的方法名称,即返回方法的名称 boolean isVarArgs():判断方法是否带可变参数,如果将此方法声明为带有可变数量的参数,则返回 true;否则,返回 false。 String toGenericString():返回描述此 Method 的字符串,包括类型参数。

getReturnType方法/getGenericReturnType方法都是获取Method对象表示的方法的返回类型,只不过前者返回的Class类型后者返回的Type(前面已分析过),Type就是一个接口而已,在Java8中新增一个默认的方法实现,返回的就参数类型信息。

public interface Type {
    // 1.8新增
    default String getTypeName() {
        return toString();
    }
}
复制代码

而getParameterTypes/getGenericParameterTypes也是同样的道理,都是获取Method对象所表示的方法的参数类型,其他方法与前面的Field和Constructor是类似的。

image.png

反射机制执行的流程

我们先来看一段代码

public class HelloReflect {
    public static void main(String[] args) {
        try {
            // 1\. 使用外部配置的实现,进行动态加载类
            HelloReflect test = (HelloReflect) Class.forName("com.example.HelloReflect").newInstance();
            test.sayHello("call directly");

            // 2\. 根据配置的函数名,进行方法调用(不需要通用的接口抽象)
            Method method = HelloReflect.class.getDeclaredMethod("sayHello", String.class);
            method.invoke(test, "method invoke");
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public void sayHello(String word) {
        System.out.println("hello," + word);
    }
}
复制代码

结果显而易见,都能正常调用,接下来我们分析一下执行流程:

image.png

从执行流程图中宏观看,一共有4个步骤:

  1. 调用Class.forName来获取Class;
  2. 调用newInstance来实例化对象;
  3. 调用getMethod来获取属性方法;
  4. 调用invoke来执行方法;

接下来我们逐步地深入它。

深度分析:forName方法与类加载

@CallerSensitive
public static Class<?> forName(String className) throws ClassNotFoundException {
    return forName0(className, true,
                    ClassLoader.getClassLoader(Reflection.getCallerClass()));
}
private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader);
复制代码

forName()反射获取类信息,并没有将实现留给了java,而是交给了jvm去加载!

那么,在这里我们还有一个问题,为什么需要ClassLoader呢? 如果我们的类是初次加载,那么是需要我们用ClassLoader去进行加载类的; 如果我们的类被加载过了,那么JVM依然尝试使用ClassLoader去进行加载类,在具体的加载方法做幂等;

下面继续源码分析:

public abstract class ClassLoader {
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {// Synchronized关键词 + getClassLoadingLock(内部:ConcurrentHashMap实现) + findLoadedClass(native方法)方法  来保证只被初始化一次
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {// 双亲委派模型
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
    protected Object getClassLoadingLock(String className) {
        Object lock = this;
        if (parallelLockMap != null) { // ConcurrentHashMap实现
            Object newLock = new Object();
            lock = parallelLockMap.putIfAbsent(className, newLock);
            if (lock == null) {
                lock = newLock;
            }
        }
        return lock;
    }
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
}
复制代码

小结:

  • ClassLoader有两个方法:findClass、loadClass,是因为历史原因,findClass是随着Java发布则出现的,而另一个loadClass则是双亲委派模型在JDK1.2的时候出现的,为了兼容之前的老方法findClass,所以有两个。
  • loadClass内部使用Synchronized + ConcurrentHashMap + findLoadedClass方法共同保证一个类在同一个类加载器下只加载一次。(强调在同一个类加载器下)
  • loadClass内部使用双亲委派模型加载。

对于类加载、双亲委派模型,具体请参考这篇文章:www.yuque.com/javahome/ja…

深度分析:newInstance方法

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    private volatile transient Constructor<T> cachedConstructor;
    private volatile transient SoftReference<ReflectionData<T>> reflectionData;
    @CallerSensitive
    public T newInstance()
        throws InstantiationException, IllegalAccessException
    {// 1、安全检查
        if (System.getSecurityManager() != null) {
            checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
        }

        // NOTE: the following code may not be strictly correct under
        // the current Java memory model.

        // Constructor lookup// cachedConstructor是一个类对象的属性(使用属性缓存起来),这个属性存放这个类的构造方法Constructor
        if (cachedConstructor == null) {
            if (this == Class.class) {// 想要实例化一个Class对象?对不起不允许,只有JVM能生成。
                throw new IllegalAccessException(
                    "Can not call newInstance() on the Class for java.lang.Class"
                );
            }
            try {// 准备获取无参构造。如果不存在则抛出NoSuchMethodException异常
                Class<?>[] empty = {};
                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
                // Disable accessibility checks on the constructor
                // since we have to do the security check here anyway
                // (the stack depth is wrong for the Constructor's
                // security check to work)
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                            c.setAccessible(true);
                            return null;
                        }
                    });
                cachedConstructor = c;// 放入缓存
            } catch (NoSuchMethodException e) {
                throw (InstantiationException)
                    new InstantiationException(getName()).initCause(e);
            }
        }
        Constructor<T> tmpConstructor = cachedConstructor;
        // Security check (same as in java.lang.reflect.Constructor)
        int modifiers = tmpConstructor.getModifiers();
        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            if (newInstanceCallerCache != caller) {
                Reflection.ensureMemberAccess(caller, this, null, modifiers);
                newInstanceCallerCache = caller;
            }
        }
        // Run constructor // 调用构造器的newInstance方法来初始化一个对象。
        try {
            return tmpConstructor.newInstance((Object[])null);
        } catch (InvocationTargetException e) {
            Unsafe.getUnsafe().throwException(e.getTargetException());
            // Not reached
            return null;
        }
    }
    private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                           int which) throws NoSuchMethodException
    {// 获取所有构造器,然后一一比较后返回对应的构造方法
        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                constructor.getParameterTypes())) {
                return getReflectionFactory().copyConstructor(constructor); // copy,内部其实是new了一个构造器。
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
    }

    //
    //
    // java.lang.reflect.Constructor handling
    //
    //

    // Returns an array of "root" constructors. These Constructor
    // objects must NOT be propagated to the outside world, but must
    // instead be copied via ReflectionFactory.copyConstructor.
    private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
        checkInitted();
        Constructor<T>[] res;
        ReflectionData<T> rd = reflectionData();// 获取缓存数据
        if (rd != null) {// 如果缓存存在,那么直接返回缓存数据。如果缓存不存在,那么将访问JVM拿到数据后放入缓存再返回数据
            res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        if (isInterface()) {
            @SuppressWarnings("unchecked")
            Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
            res = temporaryRes;
        } else {
            res = getDeclaredConstructors0(publicOnly);// 访问native方法,获取JVM的数据
        }
        if (rd != null) {// 放入缓存后返回
            if (publicOnly) {
                rd.publicConstructors = res;
            } else {
                rd.declaredConstructors = res;
            }
        }
        return res;
    }

    // Lazily create and cache ReflectionData
    private ReflectionData<T> reflectionData() {
        SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
        int classRedefinedCount = this.classRedefinedCount;
        ReflectionData<T> rd;
        if (useCaches &&
            reflectionData != null &&
            (rd = reflectionData.get()) != null &&
            rd.redefinedCount == classRedefinedCount) {
            return rd;
        }
        // else no SoftReference or cleared SoftReference or stale ReflectionData
        // -> create and replace new instance
        return newReflectionData(reflectionData, classRedefinedCount);
    }

    // 新创建缓存,保存反射信息
    private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
                                                int classRedefinedCount) {
        if (!useCaches) return null;

        while (true) {// 使用cas保证更新的线程安全性,所以反射是保证线程安全的
            ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
            // try to CAS it...
            if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
                return rd;
            }
            // else retry // 先使用CAS更新,如果更新成功,则立即返回,否则测查当前已被其他线程更新的情况,如果和自己想要更新的状态一致,则也算是成功了
            oldReflectionData = this.reflectionData;
            classRedefinedCount = this.classRedefinedCount;
            if (oldReflectionData != null &&
                (rd = oldReflectionData.get()) != null &&
                rd.redefinedCount == classRedefinedCount) {
                return rd;
            }
        }
    }

    // 反射的缓存数据结构
    // reflection data that might get invalidated when JVM TI RedefineClasses() is called
    private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        // Intermediate results for getFields and getMethods
        volatile Field[] declaredPublicFields;
        volatile Method[] declaredPublicMethods;
        volatile Class<?>[] interfaces;

        // Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;

        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
    }
}
复制代码

由此可见,ReflectionData是一个缓存类,而真正使用的缓存是一个软引用对象:SoftReference<ReflectionData>,软引用的泛型对象为ReflectionData。既然定义是缓存,那么将其设计为软引用当然是没问题的,目的是为了避免缓存数据过大占用内存可能导致OOM。

关于更多的强引用、软引用、弱引用、虚引用请参考:www.yuque.com/javahome/ja…

public final class Constructor<T> extends Executable {
    Constructor<T> copy() {
        // This routine enables sharing of ConstructorAccessor objects
        // among Constructor objects which refer to the same underlying
        // method in the VM. (All of this contortion is only necessary
        // because of the "accessibility" bit in AccessibleObject,
        // which implicitly requires that new java.lang.reflect
        // objects be fabricated for each reflective call on Class
        // objects.)
        Constructor<T> res = new Constructor<>(clazz,
                                               parameterTypes,
                                               exceptionTypes, modifiers, slot,
                                               signature,
                                               annotations,
                                               parameterAnnotations);
        res.root = this;
        // Might as well eagerly propagate this if already present
        res.constructorAccessor = constructorAccessor;
        return res;
    }
}
复制代码

Class的newInstance() 主要做了三件事:

  1. 安全检查、权限检测,如果不通过直接抛出异常;
  2. 查找无参构造器,如果缓存有,直接使用缓存的无参构造;如果缓存没有,则调JVM获取构造器并将其缓存起来,并通过匹配比较的方式再copy返回构造器(注意:如果缓存没有的话,这里copy了一份返回);
  3. 使用构造器调用构造器的newInstance()方法,生成实例并返回;

深度分析:getMethods方法

public final class Class<T> implements java.io.Serializable,
    GenericDeclaration,
    Type,
    AnnotatedElement {
    @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        // 主要做三件事:
        // 1\. 安全检查
        // 2\. 获取方法列表(有缓存)
        // 3\. copy返回
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }

    // Returns an array of "root" methods. These Method objects must NOT
    // be propagated to the outside world, but must instead be copied
    // via ReflectionFactory.copyMethod.
    private Method[] privateGetPublicMethods() {
        checkInitted();
        Method[] res;
        ReflectionData<T> rd = reflectionData(); // 获取缓存数据,如果存在则直接返回,不存在则从JVM获取
        if (rd != null) {
            res = rd.publicMethods;
            if (res != null) return res;
        }

        // No cached value available; compute value recursively.
        // Start by fetching public declared methods
        MethodArray methods = new MethodArray();
        {
            Method[] tmp = privateGetDeclaredMethods(true);
            methods.addAll(tmp);
        }
        // Now recur over superclass and direct superinterfaces.
        // Go over superinterfaces first so we can more easily filter
        // out concrete implementations inherited from superclasses at
        // the end.
        MethodArray inheritedMethods = new MethodArray();
        Class<?>[] interfaces = getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            inheritedMethods.addAllNonStatic(interfaces[i].privateGetPublicMethods());
        }
        if (!isInterface()) {
            Class<?> c = getSuperclass();
            if (c != null) {
                MethodArray supers = new MethodArray();
                supers.addAll(c.privateGetPublicMethods());
                // Filter out concrete implementations of any
                // interface methods
                for (int i = 0; i < supers.length(); i++) {
                    Method m = supers.get(i);
                    if (m != null && !Modifier.isAbstract(m.getModifiers())) {
                        inheritedMethods.removeByNameAndSignature(m);
                    }
                }
                // Insert superclass's inherited methods before
                // superinterfaces' to satisfy getMethod's search
                // order
                supers.addAll(inheritedMethods);
                inheritedMethods = supers;
            }
        }
        // Filter out all local methods from inherited ones
        for (int i = 0; i < methods.length(); i++) {
            Method m = methods.get(i);
            inheritedMethods.removeByNameAndSignature(m);
        }
        methods.addAllIfNotPresent(inheritedMethods);
        methods.compactAndTrim();
        res = methods.getArray();
        if (rd != null) {
            rd.publicMethods = res;
        }
        return res;
    }
}
复制代码
public final class Method extends Executable {
    /**
     * Package-private routine (exposed to java.lang.Class via
     * ReflectAccess) which returns a copy of this Method. The copy's
     * "root" field points to this Method.
     */
    Method copy() {
        // This routine enables sharing of MethodAccessor objects
        // among Method objects which refer to the same underlying
        // method in the VM. (All of this contortion is only necessary
        // because of the "accessibility" bit in AccessibleObject,
        // which implicitly requires that new java.lang.reflect
        // objects be fabricated for each reflective call on Class
        // objects.)
        Method res = new Method(clazz, name, parameterTypes, returnType,
            exceptionTypes, modifiers, slot, signature,
            annotations, parameterAnnotations, annotationDefault);
        res.root = this;
        // Might as well eagerly propagate this if already present
        res.methodAccessor = methodAccessor;
        return res;
    }
}
复制代码

正如代码注释上所说,Class的getMethods() 主要做三件事:

  1. 安全检查
  2. 获取方法列表(使用软引用缓存)
  3. copy返回

深度分析:getDeclaredMethods方法

public final class Class<T> implements java.io.Serializable,
    GenericDeclaration,
    Type,
    AnnotatedElement {
    @CallerSensitive
    public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyMethods(privateGetDeclaredMethods(false));
    }
}
复制代码

其实仔细一看,getDeclaredMethods()和getMethods()大同小异,原理都类似,这里我们就不再赘述了。

深度分析:Method.invoke方法

public final class Method extends Executable {
    @CallerSensitive
    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();// 这个方法里会进行初始化methodAccessor
        }
        return ma.invoke(obj, args);// 使用methodAccessor调用invoke方法,最底层是调用NativeMethodAccessorImpl的invoke方法
    }

    // NOTE that there is no synchronization used here. It is correct
    // (though not efficient) to generate more than one MethodAccessor
    // for a given Method. However, avoiding synchronization will
    // probably make the implementation more scalable.
    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);// 初始化MethodAccessor
            setMethodAccessor(tmp);
        }

        return tmp;
    }
}
复制代码
public class ReflectionFactory {
    public MethodAccessor newMethodAccessor(Method method) {
        checkInitted();

        if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
            return new MethodAccessorGenerator().
                generateMethod(method.getDeclaringClass(),// 这个方法我们在invoke中再进行分析
                    method.getName(),
                    method.getParameterTypes(),
                    method.getReturnType(),
                    method.getExceptionTypes(),
                    method.getModifiers());
        } else {
            NativeMethodAccessorImpl acc =
                new NativeMethodAccessorImpl(method);// 最终委派NativeMethodAccessorImpl类
            DelegatingMethodAccessorImpl res =
                new DelegatingMethodAccessorImpl(acc);
            acc.setParent(res);
            return res;
        }
    }
}
复制代码

研究到这里,Method.invoke()大致的流程是:

  1. 安全检查;
  2. 获取MethodAccessor对象(缓存属性,如果存在则直接使用,如果不存在则使用ReflectionFactory.newMethodAccessor进行创建一个新的MethodAccessor对象);
  3. 使用MethodAccessor对象调用invoke()方法(最终委派NativeMethodAccessorImpl.invoke());

你以为这就完了吗?还没有,继续分析NativeMethodAccessorImpl.invoke():

public interface MethodAccessor {
    /** Matches specification in {@link java.lang.reflect.Method} */
    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException;
}

class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private Method method;
    private DelegatingMethodAccessorImpl parent;// 这是一个间接层,方便在native与java版的MethodAccessor之间实现切换
    private int numInvocations;

    NativeMethodAccessorImpl(Method method) {
        this.method = method;
    }

    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
        // We can't inflate methods belonging to vm-anonymous classes because
        // that kind of class can't be referred to by name, hence can't be
        // found from the generated bytecode.
        if (++numInvocations > ReflectionFactory.inflationThreshold()// 每次调用时,次数计数器加一,一旦超过阈值,则通过generateMethod方法生成Java版的MethodAccessor的实现类,并设置为delegate对象,这样下次执行Method.invoke时,就调用新建的MethodAccessor对象的invoke()方法了。
            && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
            MethodAccessorImpl acc = (MethodAccessorImpl)
                new MethodAccessorGenerator().
                    generateMethod(method.getDeclaringClass(),// generateMethod() 是生成具体类的方法
                        method.getName(),
                        method.getParameterTypes(),
                        method.getReturnType(),
                        method.getExceptionTypes(),
                        method.getModifiers());
            parent.setDelegate(acc);
        }

        return invoke0(method, obj, args);// invoke0是一个native方法,最终由jvm进行调用业务方法!从而完成反射调用功能!
    }

    void setParent(DelegatingMethodAccessorImpl parent) {
        this.parent = parent;
    }

    private static native Object invoke0(Method m, Object obj, Object[] args);
}
复制代码

这里有一段很核心的代码:

if (++numInvocations > ReflectionFactory.inflationThreshold()// 每次调用时,次数计数器加一,一旦超过阈值,则通过generateMethod方法生成Java版的MethodAccessor的实现类,并设置为delegate对象,这样下次执行Method.invoke时,就调用新建的MethodAccessor对象的invoke()方法了。
            && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
复制代码

我们查看ReflectionFactory.inflationThreshold(),默认值是15,那么也就是说执行这行代码15次之前不会调用if语句,15次之后会调用if语句。

可以回顾看一下流程图:执行流程

说的再明白一点,15次之前由JVM调用(),15次之后由Java的新ClassLoader生成()后调用。

生成新的ClassLoader是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载,从其设计来看本身就不希望这些类一直存在内存里的,在需要的时候有就行了。

我们看一下官方的注释是如何解释的:

We define generated code into a new class loader which

delegates to the defining loader of the target class. It is

necessary for the VM to be able to resolve references to the

target class from the generated bytecodes, which could not occur

if the generated code was loaded into the bootstrap class

loader.

There are two primary reasons for creating a new loader

instead of defining these bytecodes directly into the defining

loader of the target class: first, it avoids any possible

security risk of having these bytecodes in the same loader.

Second, it allows the generated bytecodes to be unloaded earlier

than would otherwise be possible, decreasing run-time

footprint.

翻译过来就是:

我们将生成的代码定义到一个新的类加载器中,该类加载器委托给目标类的定义加载器。VM必须能够从生成的字节码解析对目标类的引用,如果生成的代码加载到引导类加载器中,就不会发生这种情况

创建一个新的加载器而不是将这些字节码直接定义到目标类的定义加载器中有两个主要原因:第一,它避免了将这些字节码放在同一加载器中可能存在的安全风险。其次,它允许生成的字节码比其他方式更早卸载,从而减少运行时占用空间

接下来继续分析generateMethod():

class MethodAccessorGenerator extends AccessorGenerator {
    public MethodAccessor generateMethod(Class<?> declaringClass,
                                         String   name,
                                         Class<?>[] parameterTypes,
                                         Class<?>   returnType,
                                         Class<?>[] checkedExceptions,
                                         int modifiers)
    {
        return (MethodAccessor) generate(declaringClass,
            name,
            parameterTypes,
            returnType,
            checkedExceptions,
            modifiers,
            false,
            false,
            null);
    }

    private MagicAccessorImpl generate(final Class<?> declaringClass,
                                       String name,
                                       Class<?>[] parameterTypes,
                                       Class<?>   returnType,
                                       Class<?>[] checkedExceptions,
                                       int modifiers,
                                       boolean isConstructor,
                                       boolean forSerialization,
                                       Class<?> serializationTargetClass)
    {
        ByteVector vec = ByteVectorFactory.create();
        asm = new ClassFileAssembler(vec);
        this.declaringClass = declaringClass;
        this.parameterTypes = parameterTypes;
        this.returnType = returnType;
        this.modifiers = modifiers;
        this.isConstructor = isConstructor;
        this.forSerialization = forSerialization;

        asm.emitMagicAndVersion();

        // Constant pool entries:
        // ( * = Boxing information: optional)
        // (+  = Shared entries provided by AccessorGenerator)
        // (^  = Only present if generating SerializationConstructorAccessor)
        //     [UTF-8] [This class's name]
        //     [CONSTANT_Class_info] for above
        //     [UTF-8] "sun/reflect/{MethodAccessorImpl,ConstructorAccessorImpl,SerializationConstructorAccessorImpl}"
        //     [CONSTANT_Class_info] for above
        //     [UTF-8] [Target class's name]
        //     [CONSTANT_Class_info] for above
        // ^   [UTF-8] [Serialization: Class's name in which to invoke constructor]
        // ^   [CONSTANT_Class_info] for above
        //     [UTF-8] target method or constructor name
        //     [UTF-8] target method or constructor signature
        //     [CONSTANT_NameAndType_info] for above
        //     [CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info] for target method
        //     [UTF-8] "invoke" or "newInstance"
        //     [UTF-8] invoke or newInstance descriptor
        //     [UTF-8] descriptor for type of non-primitive parameter 1
        //     [CONSTANT_Class_info] for type of non-primitive parameter 1
        //     ...
        //     [UTF-8] descriptor for type of non-primitive parameter n
        //     [CONSTANT_Class_info] for type of non-primitive parameter n
        // +   [UTF-8] "java/lang/Exception"
        // +   [CONSTANT_Class_info] for above
        // +   [UTF-8] "java/lang/ClassCastException"
        // +   [CONSTANT_Class_info] for above
        // +   [UTF-8] "java/lang/NullPointerException"
        // +   [CONSTANT_Class_info] for above
        // +   [UTF-8] "java/lang/IllegalArgumentException"
        // +   [CONSTANT_Class_info] for above
        // +   [UTF-8] "java/lang/InvocationTargetException"
        // +   [CONSTANT_Class_info] for above
        // +   [UTF-8] "<init>"
        // +   [UTF-8] "()V"
        // +   [CONSTANT_NameAndType_info] for above
        // +   [CONSTANT_Methodref_info] for NullPointerException's constructor
        // +   [CONSTANT_Methodref_info] for IllegalArgumentException's constructor
        // +   [UTF-8] "(Ljava/lang/String;)V"
        // +   [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/String;)V"
        // +   [CONSTANT_Methodref_info] for IllegalArgumentException's constructor taking a String
        // +   [UTF-8] "(Ljava/lang/Throwable;)V"
        // +   [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/Throwable;)V"
        // +   [CONSTANT_Methodref_info] for InvocationTargetException's constructor
        // +   [CONSTANT_Methodref_info] for "super()"
        // +   [UTF-8] "java/lang/Object"
        // +   [CONSTANT_Class_info] for above
        // +   [UTF-8] "toString"
        // +   [UTF-8] "()Ljava/lang/String;"
        // +   [CONSTANT_NameAndType_info] for "toString()Ljava/lang/String;"
        // +   [CONSTANT_Methodref_info] for Object's toString method
        // +   [UTF-8] "Code"
        // +   [UTF-8] "Exceptions"
        //  *  [UTF-8] "java/lang/Boolean"
        //  *  [CONSTANT_Class_info] for above
        //  *  [UTF-8] "(Z)V"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "booleanValue"
        //  *  [UTF-8] "()Z"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "java/lang/Byte"
        //  *  [CONSTANT_Class_info] for above
        //  *  [UTF-8] "(B)V"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "byteValue"
        //  *  [UTF-8] "()B"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "java/lang/Character"
        //  *  [CONSTANT_Class_info] for above
        //  *  [UTF-8] "(C)V"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "charValue"
        //  *  [UTF-8] "()C"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "java/lang/Double"
        //  *  [CONSTANT_Class_info] for above
        //  *  [UTF-8] "(D)V"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "doubleValue"
        //  *  [UTF-8] "()D"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "java/lang/Float"
        //  *  [CONSTANT_Class_info] for above
        //  *  [UTF-8] "(F)V"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "floatValue"
        //  *  [UTF-8] "()F"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "java/lang/Integer"
        //  *  [CONSTANT_Class_info] for above
        //  *  [UTF-8] "(I)V"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "intValue"
        //  *  [UTF-8] "()I"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "java/lang/Long"
        //  *  [CONSTANT_Class_info] for above
        //  *  [UTF-8] "(J)V"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "longValue"
        //  *  [UTF-8] "()J"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "java/lang/Short"
        //  *  [CONSTANT_Class_info] for above
        //  *  [UTF-8] "(S)V"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above
        //  *  [UTF-8] "shortValue"
        //  *  [UTF-8] "()S"
        //  *  [CONSTANT_NameAndType_info] for above
        //  *  [CONSTANT_Methodref_info] for above

        short numCPEntries = NUM_BASE_CPOOL_ENTRIES + NUM_COMMON_CPOOL_ENTRIES;
        boolean usesPrimitives = usesPrimitiveTypes();
        if (usesPrimitives) {
            numCPEntries += NUM_BOXING_CPOOL_ENTRIES;
        }
        if (forSerialization) {
            numCPEntries += NUM_SERIALIZATION_CPOOL_ENTRIES;
        }

        // Add in variable-length number of entries to be able to describe
        // non-primitive parameter types and checked exceptions.
        numCPEntries += (short) (2 * numNonPrimitiveParameterTypes());

        asm.emitShort(add(numCPEntries, S1));

        final String generatedName = generateName(isConstructor, forSerialization);
        asm.emitConstantPoolUTF8(generatedName);
        asm.emitConstantPoolClass(asm.cpi());
        thisClass = asm.cpi();
        if (isConstructor) {
            if (forSerialization) {
                asm.emitConstantPoolUTF8
                    ("sun/reflect/SerializationConstructorAccessorImpl");
            } else {
                asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");
            }
        } else {
            asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");
        }
        asm.emitConstantPoolClass(asm.cpi());
        superClass = asm.cpi();
        asm.emitConstantPoolUTF8(getClassName(declaringClass, false));
        asm.emitConstantPoolClass(asm.cpi());
        targetClass = asm.cpi();
        short serializationTargetClassIdx = (short) 0;
        if (forSerialization) {
            asm.emitConstantPoolUTF8(getClassName(serializationTargetClass, false));
            asm.emitConstantPoolClass(asm.cpi());
            serializationTargetClassIdx = asm.cpi();
        }
        asm.emitConstantPoolUTF8(name);
        asm.emitConstantPoolUTF8(buildInternalSignature());
        asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
        if (isInterface()) {
            asm.emitConstantPoolInterfaceMethodref(targetClass, asm.cpi());
        } else {
            if (forSerialization) {
                asm.emitConstantPoolMethodref(serializationTargetClassIdx, asm.cpi());
            } else {
                asm.emitConstantPoolMethodref(targetClass, asm.cpi());
            }
        }
        targetMethodRef = asm.cpi();
        if (isConstructor) {
            asm.emitConstantPoolUTF8("newInstance");
        } else {
            asm.emitConstantPoolUTF8("invoke");
        }
        invokeIdx = asm.cpi();
        if (isConstructor) {
            asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");
        } else {
            asm.emitConstantPoolUTF8
                ("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
        }
        invokeDescriptorIdx = asm.cpi();

        // Output class information for non-primitive parameter types
        nonPrimitiveParametersBaseIdx = add(asm.cpi(), S2);
        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> c = parameterTypes[i];
            if (!isPrimitive(c)) {
                asm.emitConstantPoolUTF8(getClassName(c, false));
                asm.emitConstantPoolClass(asm.cpi());
            }
        }

        // Entries common to FieldAccessor, MethodAccessor and ConstructorAccessor
        emitCommonConstantPoolEntries();

        // Boxing entries
        if (usesPrimitives) {
            emitBoxingContantPoolEntries();
        }

        if (asm.cpi() != numCPEntries) {
            throw new InternalError("Adjust this code (cpi = " + asm.cpi() +
                ", numCPEntries = " + numCPEntries + ")");
        }

        // Access flags
        asm.emitShort(ACC_PUBLIC);

        // This class
        asm.emitShort(thisClass);

        // Superclass
        asm.emitShort(superClass);

        // Interfaces count and interfaces
        asm.emitShort(S0);

        // Fields count and fields
        asm.emitShort(S0);

        // Methods count and methods
        asm.emitShort(NUM_METHODS);

        emitConstructor();
        emitInvoke();

        // Additional attributes (none)
        asm.emitShort(S0);

        // Load class
        vec.trim();
        final byte[] bytes = vec.getData();
        // Note: the class loader is the only thing that really matters
        // here -- it's important to get the generated code into the
        // same namespace as the target class. Since the generated code
        // is privileged anyway, the protection domain probably doesn't
        // matter.
        return AccessController.doPrivileged(
            new PrivilegedAction<MagicAccessorImpl>() {
                public MagicAccessorImpl run() {
                    try {
                        return (MagicAccessorImpl)
                            ClassDefiner.defineClass
                                (generatedName,
                                    bytes,
                                    0,
                                    bytes.length,
                                    declaringClass.getClassLoader()).newInstance();
                    } catch (InstantiationException | IllegalAccessException e) {
                        throw new InternalError(e);
                    }
                }
            });
    }
}
复制代码

generate方法很长,我们只关注重点:

return (MagicAccessorImpl)
    ClassDefiner.defineClass
        (generatedName,
            bytes,
            0,
            bytes.length,
            declaringClass.getClassLoader()).newInstance();
复制代码
class ClassDefiner {
    static Class<?> defineClass(String name, byte[] bytes, int off, int len,
                                final ClassLoader parentClassLoader)
    {// 每次都生成一个新的ClassLoader
        ClassLoader newLoader = AccessController.doPrivileged(
            new PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                    return new DelegatingClassLoader(parentClassLoader);
                }
            });
        return unsafe.defineClass(name, bytes, off, len, newLoader, null);
    }
}
复制代码

注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的:

JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0  
(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)  
{  
    return JVM_InvokeMethod(env, m, obj, args);  
}  
复制代码
JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))  
  JVMWrapper("JVM_InvokeMethod");  
  Handle method_handle;  
  if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) {  
    method_handle = Handle(THREAD, JNIHandles::resolve(method));  
    Handle receiver(THREAD, JNIHandles::resolve(obj));  
    objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));  
    oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL);  
    jobject res = JNIHandles::make_local(env, result);  
    if (JvmtiExport::should_post_vm_object_alloc()) {  
      oop ret_type = java_lang_reflect_Method::return_type(method_handle());  
      assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!");  
      if (java_lang_Class::is_primitive(ret_type)) {  
        // Only for primitive type vm allocates memory for java object.  
        // See box() method.  
        JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);  
      }  
    }  
    return res;  
  } else {  
    THROW_0(vmSymbols::java_lang_StackOverflowError());  
  }  
JVM_END  
复制代码

其中的关键又是Reflection::invoke_method():

// This would be nicer if, say, java.lang.reflect.Method was a subclass  
// of java.lang.reflect.Constructor  

oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) {  
  oop mirror             = java_lang_reflect_Method::clazz(method_mirror);  
  int slot               = java_lang_reflect_Method::slot(method_mirror);  
  bool override          = java_lang_reflect_Method::override(method_mirror) != 0;  
  objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror)));  

  oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror);  
  BasicType rtype;  
  if (java_lang_Class::is_primitive(return_type_mirror)) {  
    rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL);  
  } else {  
    rtype = T_OBJECT;  
  }  

  instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(mirror));  
  methodOop m = klass->method_with_idnum(slot);  
  if (m == NULL) {  
    THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");  
  }  
  methodHandle method(THREAD, m);  

  return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD);  
}  
复制代码

再下去就深入到HotSpot VM的内部了,本文就在这里打住吧。

我们分析一下反射性能:

image.png 从变化趋势上看,第1次和第16次调用是最耗时的(初始化NativeMethodAccessorImpl和字节码拼装MethodAccessorImpl)。毕竟初始化是不可避免的,而native方式的初始化会更快,因此前几次的调用会采用native方法。

随着调用次数的增加,每次反射都使用JNI跨越native边界会对优化有阻碍作用,相对来说使用拼装出的字节码可以直接以Java调用的形式实现反射,发挥了JIT优化的作用,避免了JNI为了维护OopMap(HotSpot用来实现准确式GC的数据结构)进行封装/解封装的性能损耗。因此在已经创建了MethodAccessor的情况下,使用Java版本的实现会比native版本更快。所以当调用次数到达一定次数(15次)后,会切换成Java实现的版本,来优化未来可能的更频繁的反射调用。

深入研究Class类

Class类一共有2个公公共静态方法和60个公共属性方法。

2个公共静态方法:

/**
 * Returns the {@code Class} object associated with the class or
 * interface with the given string name.  Invoking this method is
 * equivalent to:
 *
 * <blockquote>
 *  {@code Class.forName(className, true, currentLoader)}
 * </blockquote>
 *
 * where {@code currentLoader} denotes the defining class loader of
 * the current class.
 *
 * <p> For example, the following code fragment returns the
 * runtime {@code Class} descriptor for the class named
 * {@code java.lang.Thread}:
 *
 * <blockquote>
 *   {@code Class t = Class.forName("java.lang.Thread")}
 * </blockquote>
 * <p>
 * A call to {@code forName("X")} causes the class named
 * {@code X} to be initialized.
 *
 * @param      className   the fully qualified name of the desired class.
 * @return     the {@code Class} object for the class with the
 *             specified name.
 * @exception LinkageError if the linkage fails
 * @exception ExceptionInInitializerError if the initialization provoked
 *            by this method fails
 * @exception ClassNotFoundException if the class cannot be located
 */
public static Class<?> forName(String className) throws ClassNotFoundException 
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException
复制代码

60个公共属性方法:

public String toString()
public T newInstance() throws InstantiationException, IllegalAccessException
public native boolean isInstance(Object obj);
public native boolean isAssignableFrom(Class<?> cls);
public native boolean isInterface();
public native boolean isArray();
public native boolean isPrimitive();
public boolean isAnnotation()
public boolean isSynthetic()
public String getName()
public ClassLoader getClassLoader()
public TypeVariable<Class<T>>[] getTypeParameters()
public native Class<? super T> getSuperclass();
public Type getGenericSuperclass()
public Package getPackage()
public Class<?>[] getInterfaces()
public Type[] getGenericInterfaces()
public native Class<?> getComponentType();
public native int getModifiers();
public native Object[] getSigners();
public Method getEnclosingMethod() throws SecurityException
public Constructor<?> getEnclosingConstructor() throws SecurityException
public Class<?> getDeclaringClass() throws SecurityException
public Class<?> getEnclosingClass() throws SecurityException
public String getSimpleName()
public String getTypeName()
public String getCanonicalName()
public boolean isAnonymousClass()
public boolean isLocalClass()
public boolean isMemberClass()
public Class<?>[] getClasses()
public Field[] getFields() throws SecurityException
public Method[] getMethods() throws SecurityException
public Constructor<?>[] getConstructors() throws SecurityException
public Field getField(String name) throws NoSuchFieldException, SecurityException
public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
public Class<?>[] getDeclaredClasses() throws SecurityException
public Field[] getDeclaredFields() throws SecurityException
public Method[] getDeclaredMethods() throws SecurityException
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
public InputStream getResourceAsStream(String name)
public java.net.URL getResource(String name)
public java.security.ProtectionDomain getProtectionDomain()
public boolean desiredAssertionStatus()
public boolean isEnum()
public T[] getEnumConstants()
public T cast(Object obj)
public <U> Class<? extends U> asSubclass(Class<U> clazz)
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass)
public Annotation[] getAnnotations()
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)
public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass)
public Annotation[] getDeclaredAnnotations()
public AnnotatedType getAnnotatedSuperclass()
public AnnotatedType[] getAnnotatedInterfaces()
复制代码

看到这么多API,心里好慌啊~别急,我们就讲讲最常用的。 forName:Class类的一个静态方法,获取Class对象的一个引用。如果引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。大家请看forName的注释中包含这么一句话:A call to {@code forName(“X”)} causes the class named {@code X} to be initialized. Object-getClass():获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。 getName():取全限定的类名(包括包名),即类的完整名字。 getSimpleName():获取类名(不包括包名) getCanonicalName():获取全限定的类名(包括包名) isInterface():判断Class对象是否是表示一个接口 getInterfaces():返回Class对象数组,表示Class对象所引用的类所实现的所有接口。 getSuperclass():返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。 newInstance():返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。 getFields():获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。 getDeclaredFields():获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。

考虑篇幅原因,更多Class类请参考:www.yuque.com/javahome/ja…

总结:

  1. 反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;
  2. 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
  3. 反射也是考虑了线程安全的,放心使用;
  4. 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;
  5. 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;
  6. 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
  7. 调度反射方法,最终是由jvm执行invoke0()执行;

参考资料

猜你喜欢

转载自juejin.im/post/7067711583295635486