zoukankan      html  css  js  c++  java
  • Java反射

    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的高并发问题

     

  • 相关阅读:
    c# 类成员的定义 定义方法、字段和属性
    Set Rowcount分页查询(转)
    Sql 学习笔记
    xen与kvm 天高地厚
    Linux_free(buffer与cache区别) 天高地厚
    快照技术 天高地厚
    磁盘阵列的状态与种类 天高地厚
    在linux中使用ramdisk文件系统 天高地厚
    oracle逻辑读取 天高地厚
    BeginInvoke和EndInvoke操作线程 天高地厚
  • 原文地址:https://www.cnblogs.com/lewis09/p/10288854.html
Copyright © 2011-2022 走看看