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

     

  • 相关阅读:
    Html禁止粘贴 复制 剪切
    表单标签
    自构BeanHandler(用BeansUtils)
    spring配置中引入properties
    How Subcontracting Cockpit ME2ON creates SD delivery?
    cascadia code一款很好看的微软字体
    How condition value calculated in sap
    Code in SAP query
    SO Pricing not updated for partial billing items
    Javascript learning
  • 原文地址:https://www.cnblogs.com/lewis09/p/10288854.html
Copyright © 2020-2023  润新知