美文网首页
java反射-Method

java反射-Method

作者: 全都是泡沫啦 | 来源:发表于2018-11-30 21:34 被阅读0次

    前序

    java的反射机制增加程序的灵活性,作为许多开源框架的基础,我们有必要去了解一下它的实现,下面讲解Method

    1. 代码
    import java.lang.management.ManagementFactory;
    import java.lang.management.RuntimeMXBean;
    import java.lang.reflect.Method;
    
    public class TestProxyMethod {
        public static interface ISubject {
            void sayHi();
        }
        public static class Subject implements ISubject {
            public void sayHi() {
                System.out.println("Subject-sayHi");
            }
        }
    
        public static class SubjectEx extends Subject {
            public void sayHi() {
                System.out.println("SubjectEx-sayHi");
            }
        }
    
        public static void main(String[] args) throws Exception {
            RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
            System.out.println("pid=" + runtimeMXBean.getName().split("@")[0]);
    
            Subject subject = new Subject();
            SubjectEx subjectEx = new SubjectEx();
            //接口的实现调用
            Method sayHiI = ISubject.class.getDeclaredMethod("sayHi", new Class[]{});
            sayHiI.invoke(subject);
            sayHiI.invoke(subjectEx);
    
            Method sayHi = Subject.class.getDeclaredMethod("sayHi", new Class[]{});
            sayHi.invoke(subjectEx);
    
            Method sayHiEx = SubjectEx.class.getDeclaredMethod("sayHi", new Class[]{});
            sayHiEx.invoke(subject);
        }
    }
    运行结果
    pid=2724
    Subject-sayHi
    SubjectEx-sayHi
    Subject-sayHi
    SubjectEx-sayHi
    Exception in thread "main" java.lang.IllegalArgumentException: java.lang.ClassCastException@5f184fc6
        at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at TestProxyMethod.main(TestProxyMethod.java:42)
    
    1. 分析运行结果
    1. 当我们获取接口ISubject 的方法sayHi(),执行invoke传入子类时,打印了子类的执行结果
    2. 当我们获取类Subject 的方法sayHi(),执invoke传入当前定义方法的类和子类,打印出正常的结果
    3. 当我们获取子类SubjectEx的方法sayHi(),执行invoke传入父类subject,结果确抛出异常ClassCastException,这个异常不就是subject强转SubjectEx报的错么?
    1. 提出疑问
      反射为什么会出现多态的效果?为什么会报ClassCastException错误?
    2. 分析
        //Method的执行
        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();
            }
            return ma.invoke(obj, args);
        }
      //acquireMethodAccessor最终生成的方法
      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;
            }
        }
    

    通过代码可以看出Method的最终执行交个了MethodAccessor的Invoke方法,而MethodAccessor的生成有两种方式:第一种是MethodAccessorGenerator使用asm字节码生成,第二种是NativeMethodAccessorImpl通过jni底层jvm的实现方式,这里我们使用第一种方式进行分析,需要的条件是noInflation为true & 方法的申明类不是内部类

    private static void checkInitted() {
            if(!initted) {
                       ...
                        if(System.out == null) {
                            return null;
                        } else {
                            String var1 = System.getProperty("sun.reflect.noInflation");
                            if(var1 != null && var1.equals("true")) {
                                ReflectionFactory.noInflation = true;
                            }
    
                            var1 = System.getProperty("sun.reflect.inflationThreshold");
                            if(var1 != null) {
                                try {
                                    ReflectionFactory.inflationThreshold = Integer.parseInt(var1);
                                } catch (NumberFormatException var3) {
                                    throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", var3);
                                }
                            }
    
                            ReflectionFactory.initted = true;
                            return null;
                        }
                  .....
            }
        }
    
    1. 通过使用工具HSDB获取MethodAccessorGenerator生成的字节码文件,执行方法设置-Dsun.reflect.noInflation=true,获取到类GeneratedMethodAccessor4
    package sun.reflect;
    
    import TestProxyMethod.SubjectEx;
    import java.lang.reflect.InvocationTargetException;
    
    public class GeneratedMethodAccessor3 extends MethodAccessorImpl {
        public Object invoke(Object var1, Object[] var2) throws InvocationTargetException {
            if(var1 == null) {
                throw new NullPointerException();
            } else {
                SubjectEx var10000;
                try {
                    var10000 = (SubjectEx)var1;
                    if(var2 != null && var2.length != 0) {
                        throw new IllegalArgumentException();
                    }
                } catch (NullPointerException | ClassCastException var4) {
                    throw new IllegalArgumentException(var4.toString());
                }
    
                try {
                    var10000.sayHi();
                    return null;
                } catch (Throwable var3) {
                    throw new InvocationTargetException(var3);
                }
            }
        }
    
        public GeneratedMethodAccessor3() {
        }
    }
    

    有没有发现原来反射是这么实现的呀,恍然大雾了吧,var10000 = (SubjectEx)var1;这个的SubjectEx为方法定义的类,为什么会报错也就一目了然了吧

    1. 注意
    • 反射固然好用,但是会导致老年代的class字节码占用的内存越来越多
    • 相同的方法MethodAccessor不会无穷创建,Method.root中会保留一份,但是有并发创建的问题
        {
              tmp = reflectionFactory.newMethodAccessor(this);
              setMethodAccessor(tmp);
        }
    
       void setMethodAccessor(MethodAccessor accessor) {
            methodAccessor = accessor;
            // Propagate up
            if (root != null) {
                root.setMethodAccessor(accessor);
            }
        }
    
    • 为了生成的class不会发生冲突,java单独使用了类加载隔离,自定义DelegatingClassLoader,这也会导致老年代DelegatingClassLoader数量增多
    class ClassDefiner {
        static final Unsafe unsafe = Unsafe.getUnsafe();
    
        ClassDefiner() {
        }
    
        static Class<?> defineClass(String var0, byte[] var1, int var2, int var3, final ClassLoader var4) {
            ClassLoader var5 = (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                    return new DelegatingClassLoader(var4);
                }
            });
            return unsafe.defineClass(var0, var1, var2, var3, var5, (ProtectionDomain)null);
        }
    }
    
    class DelegatingClassLoader extends ClassLoader {
        DelegatingClassLoader(ClassLoader var1) {
            super(var1);
        }
    }
    
    

    相关文章

      网友评论

          本文标题:java反射-Method

          本文链接:https://www.haomeiwen.com/subject/xbvhcqtx.html