美文网首页
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