当前位置: 移动技术网 > IT编程>开发语言>Java > Java反射

Java反射

2019年01月19日  | 移动技术网IT编程  | 我要评论

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

首先你需要了解类加载的过程,这里我们简单提一下(加载-验证-准备-解析-初始化),反射是靠jvm和class相关类实现的。

按照这个例子,我们调试下看看具体实现。

@data
public class person {
    private string name;

    public static void main(string[] args) throws exception {
        person person = new person();
        person.setname("lewis");

        for (int i = 0; i < 16; i++) {
            method method = person.class.getmethod("getname");
            system.out.println(method.invoke(person));
        }
    }
}

 

@callersensitive
public object invoke(object obj, object... args)
    throws illegalaccessexception, illegalargumentexception,
       invocationtargetexception
{
    if (!override) {
        // 检查方法是否为public
        if (!reflection.quickcheckmemberaccess(clazz, modifiers)) {
            class<?> caller = reflection.getcallerclass();
            // 权限校验
            checkaccess(caller, clazz, obj, modifiers);
        }
    }
    // methodaccessor实现有两个版本,一个是java实现的,另一个是jni实现的
    methodaccessor ma = methodaccessor;             // read volatile
    if (ma == null) {
        ma = acquiremethodaccessor();
    }
    return ma.invoke(obj, args);
}

我们上面提到了 methodaccessor 有两个实现,java版本和jni版本(就是java native),

java实现的版本在初始化时需要较多时间,但长久来说性能较好;jni版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过java版了。
 
为了尽可能地减少性能损耗,hotspot jdk采用“inflation”的技巧:让java方法在被反射调用时,开头若干次使用jni版,等反射调用次数超过阈值(15)时则生成一个专用的methodaccessor实现类,生成其中的invoke()方法的字节码,以后对该java方法的反射调用就会使用java版本。 
 
reflectionfactory.newmethodaccessor()生产methodaccessor对象的逻辑,一开始(jni版)会生产nativemethodaccessorimpl和delegatingmethodaccessorimpl两个对象。
    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;
        }
    }
delegatingmethodaccessorimpl的源码如下:
这是一个中间层,方便在jni版本与java版的methodaccessor之间实现切换。
class delegatingmethodaccessorimpl extends methodaccessorimpl {
    private methodaccessorimpl delegate;

    delegatingmethodaccessorimpl(methodaccessorimpl var1) {
        this.setdelegate(var1);
    }

    public object invoke(object var1, object[] var2) throws illegalargumentexception, invocationtargetexception {
        return this.delegate.invoke(var1, var2);
    }

    void setdelegate(methodaccessorimpl var1) {
        this.delegate = var1;
    }
}

来看看nativemethodaccessorimpl实现,超过15次以后调用反射,就会通过我们上面提到的中间层 delegatingmethodaccessorimpl 所引用的 methodaccessor 都是java 版。

class nativemethodaccessorimpl extends methodaccessorimpl {
    private final method method;
    private delegatingmethodaccessorimpl parent;
    private int numinvocations;

    nativemethodaccessorimpl(method var1) {
        this.method = var1;
    }

    public object invoke(object var1, object[] var2) throws illegalargumentexception, invocationtargetexception {
        // 方法被调用时,程序调用计数器都会增加1,看看是否超过阈值
        if (++this.numinvocations > reflectionfactory.inflationthreshold() && !reflectutil.isvmanonymousclass(this.method.getdeclaringclass())) {
            // 超过15次 则调用methodaccessorgenerator.generatemethod()来生成java版的methodaccessor的实现类
            // 并且改变通过中间层,后续delegatingmethodaccessorimpl所引用的methodaccessor改为java版
            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版本,jni方式调用
        return invoke0(this.method, var1, var2);
    }

    void setparent(delegatingmethodaccessorimpl var1) {
        this.parent = var1;
    }

    private static native object invoke0(method var0, object var1, object[] var2);
}

在默认情况下,方法的反射调用为委派实现,委派给本地实现来进行方法调用。在调用超过 15 次之后,委派实现便会将委派对象切换至动态实现。这个动态的字节码是在java运行过程中通过asm自动生成的,它将直接使用 invoke 指令来调用目标方法。

继续查看代码,可以看到sun.reflect.methodaccessorgenerator#generate的实现是调用asm字节码增强工具来生成类,此过程较长,不在此列出。在该方法的最后,我们发现有这样一个操作sun.reflect.classdefiner#defineclass,查看其源码

static class<?> defineclass(string name, byte[] bytes, int off, int len,
                                final classloader parentclassloader)
    {
        // 创建一个delegatingclassloader用来加载生成的类
        classloader newloader = accesscontroller.doprivileged(
            new privilegedaction<classloader>() {
                public classloader run() {
                        return new delegatingclassloader(parentclassloader);
                    }
                });
        return unsafe.defineclass(name, bytes, off, len, newloader, null);
}

 

参考:

 

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网