Java反射

2019-01-21 02:40:32来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

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);
}

 

参考:

一个关于log4j2的高并发问题

 

 


原文链接:https://www.cnblogs.com/lewis09/p/10288854.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:Swing 自定义JTable 多选框 自动选择的错误

下一篇:SpringMVC中参数的传递