美文网首页架构师成长记
一文读懂系列-Java反射

一文读懂系列-Java反射

作者: monkey01 | 来源:发表于2018-03-01 11:22 被阅读28次

如何使用反射

在开发过程中我们经常会使用到反射,反射使用起来也很简单,在java runtime下通过方法名和参数就可以反射获取到相应的方法,并通过方法实例的invoke方法调用这个反射出来的方法。当然也可以通过全路径类名反射获取到类实例,这对于我们开发一些中间件或者框架是非常有用的,这样就实现了很多动态的黑科技。

public class ReflectTest {

    public static void main(String[] args) throws Exception {
        Proxy target = new Proxy();
        Method method = Proxy.class.getDeclaredMethod("hello");
        method.invoke(target);
        
        Student student = Class.forName("com.monkey01.Student");
        student.run();
    }

    static class Proxy {
        public void hello() {
            System.out.println("hello world!");
        }
    }
}

反射实现源码分析

getDeclaredMethod

反射获取类方法是通过getDeclaredMethod()方法来实现的,这个方法的入参为name方法名,parameterTypes参数类型,参数类型是可变参数。
getDeclaredMethod实现中首先检查反射的方法是否是声明为可访问类型,对于private的方法无法反射访问,会抛出异常。先通过调用privateGetDeclaredMethods方法获取到所有的方法列表,再将获取到的方法列表作为searchMethods方法的入参,进行方法查找。

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

privateGetDeclaredFields

privateGetDeclaredFields方法为Class类的内部方法,通过reflectionData()方法从JVM或者缓存中获取方法列表。

private Field[] privateGetDeclaredFields(boolean publicOnly) {
        checkInitted();
        Field[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.declaredPublicFields : rd.declaredFields;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        res = Reflection.filterFields(this, getDeclaredFields0(publicOnly));
        if (rd != null) {
            if (publicOnly) {
                rd.declaredPublicFields = res;
            } else {
                rd.declaredFields = res;
            }
        }
        return res;
    }

先从缓存中获取方法列表,如果没有泽通过newReflectionData方法创建新的反射对象,所有的反射对象都继承自ReflectionData类。

// Lazily create and cache ReflectionData
    private ReflectionData<T> reflectionData() {
        SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
        int classRedefinedCount = this.classRedefinedCount;
        ReflectionData<T> rd;
        if (useCaches &&
            reflectionData != null &&
            (rd = reflectionData.get()) != null &&
            rd.redefinedCount == classRedefinedCount) {
            return rd;
        }
        // else no SoftReference or cleared SoftReference or stale ReflectionData
        // -> create and replace new instance
        return newReflectionData(reflectionData, classRedefinedCount);
    }

下面我们看下ReflectionData这个内部类,ReflectionData是用来缓存从JVM里读取类的属性数据。ReflectionData对象是SoftReference类型的,因为软引用类型在VM资源紧张的时候会被自动回收,所以在使用的时候还是要注意下。

private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        // Intermediate results for getFields and getMethods
        volatile Field[] declaredPublicFields;
        volatile Method[] declaredPublicMethods;
        volatile Class<?>[] interfaces;

        // Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;

        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
    }

searchMethods

searchMethods方法实现比较简单就是根据方法名称和入参类型进行筛选,返回符合条件的一个方法对象,如果找到一个Method则copy一个返回。

private static Method searchMethods(Method[] methods,
                                        String name,
                                        Class<?>[] parameterTypes)
    {
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }

copyMethod

getReflectionFactory的copyMethod底层调用了Method的copy方法。

public Method   copyMethod(Method arg) {
        return arg.copy();
    }

copy

每次调用getDeclaredMethod方法返回的Method对象都是copy方法new的一个新对象,并且对象的root属性都指向原来的Method对象,所以如果要经常调用,最好把Method对象缓存起来提高性能。

Method copy() {
        if (this.root != null)
            throw new IllegalArgumentException("Can not copy a non-root Method");

        Method res = new Method(clazz, name, parameterTypes, returnType,
                                exceptionTypes, modifiers, slot, signature,
                                annotations, parameterAnnotations, annotationDefault);
        res.root = this;
        // Might as well eagerly propagate this if already present
        res.methodAccessor = methodAccessor;
        return res;
    }

invoke

从Method的invoke方法源码可以发现,通过Method对象调用方法的时候核心是通过Method对象里的methodAccessor对象去调用具体方法。

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

MethodAccessor是个接口有NativeMethodAccessorImpl、DelegatingMethodAccessorImpl、MethodAccessorImpl 3个实现,下面我们看下最核心的NativeMethodAccessorImpl,其他几种实现最终都会到原生方法访问实现类中的invoke方法,invoke底层其实和forName一样会通过jni去调用.dll动态链接库的方法invoke0来实现方法的调用,native其实是在JVM的栈中找到这个方法,然后通过方法实例根据参数进行方法运行。

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        if(++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
            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);
        }

        return invoke0(this.method, var1, var2);
    }

forName

forName方法也是我们使用反射时候经常用到的方法,通过forName可以反射动态反射获取到对象实例,forName也有3个重载方法,可以使用JVM默认的classLoader加载类,也可以通过自定义的ClassLoader去加载类。可以看到每个重载方法最后都会调用到forName0方法,这个方法也是native方法,是用jni去调用.dll动态链接库的方法,所以通过java反编译工具是看不到具体源码的,需要下载源码包才能看到。native方法底层实现了在JVM中通过指定的classloader类加载器根据类的全路径去找到已经编译好的class并去动态创建这个类的实例。

public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

总结

反射其实看起来比较神奇,但是看了源码后会发现其实内部不是特别复杂,不过在我们写的中间件和框架中经常会用到这个黑科技,包括很多其他语言也有类似运行时反射的机制,这也是一种比较灵活和方便模块解耦的方法,我们在工作中如果遇到需要模块解耦或者需要动态创建类的地方可以考虑采用反射机制来实现相关功能。

相关文章

  • 一文读懂系列-Java反射

    如何使用反射 在开发过程中我们经常会使用到反射,反射使用起来也很简单,在java runtime下通过方法名和参数...

  • Java系列 - 反射

    一、为什么需要反射 反射是体现java语言动态性最重要的特征,举几个例子: 1、服务器框架的动态配置能力 - 用反...

  • Java基础:注解

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 1. 概述 注解...

  • Java反射机制详解(一)

    接下来我们将介绍Java反射机制的一系列的知识。本篇文章主要针对Java反射机制的介绍以及反射API的使用知识。 ...

  • java 源码系列 - 带你读懂 Reference 和 Ref

    java 源码系列 - 带你读懂 Reference 和 ReferenceQueue https://blog....

  • Java基础:动态代理

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 概述 在运行时,...

  • Java基础:类加载器

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 1. 什么是类加...

  • 【阿里P8大牛教你Android入门之路(java篇)】Java

    【阿里P8大牛教你Android入门之路(java篇)】Java集合——Java反射1(系列篇7)链接:https...

  • java系列23:反射

    反射:使用类的字节码所做的一系列操作。 源代码编译过后的字节码文件(.class文件),在java中也被视为对象,...

  • Java进阶系列——反射

    学习笔记,基础知识。开头文字很长,不要着急,干货会有的,先弄清楚概念,理解与使用起来就会事半功倍,不会纠结什么时候...

网友评论

    本文标题:一文读懂系列-Java反射

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