一篇文章讲清楚Java中的反射

news/2024/9/19 19:23:21/文章来源:https://www.cnblogs.com/seven97-top/p/18387600

介绍

每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。

类加载相当于 Class 对象的加载。类在第一次使用时才动态加载到 JVM 中,可以使用 Class.forName("com.mysql.jdbc.Driver") 这种方式来控制类的加载,该方法会返回一个 Class 对象。

反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。

Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:

  • Field

  • Method

  • Constructor

Java反射机制是指在运行时动态地获取一个类的信息并能够操作该类的属性和方法的能力。Java反射机制使得程序能够在运行时借助Class类的API来操作自身的属性和方法,从而大大增强了Java的灵活性和可扩展性。

优缺点

优点:

  1. 提高了程序的灵活性:可以在运行时获取和操作类的信息,使程序具有更好的灵活性和扩展性。
  2. 减少了代码的重复性:可以动态地获取和操作类的信息,减少了代码的重复性。
  3. 提高了程序的可维护性:可以使程序的结构更加清晰明了,提高了程序的可维护性。

缺点:

  1. 性能较低:Java反射机制是通过运行时动态获取和操作类的信息,性能较低。
  2. 安全性问题:Java反射机制可以访问和操作类的所有信息,存在安全性问题。
public void test2() {long start = System.currentTimeMillis();for (int i = 0; i < 10000000; i++) {getAuthor(); //运行时间15
//       getAuthorByReflect();//运行时间4378}long end = System.currentTimeMillis();System.out.println("运行时间:" + (end - start));
}

如何获取

//1. 通过类名.class获取 
class s = SubjectService.class;//2.通过Class.forName名获取
class s = Class.forName(SubjectService);//3.通过对象.getclass获取
class s = data.getClass();//4.通过类加载器获取
ClassLoader classLoader = Practice0.class.getClassLoader();
Class s = classLoader.loadClass("全类名");

比较四种获取方式的区别?通过类加载器获取的方式不常用,在此不做比较。

  1. 类名.class:JVM将使用类装载器,将类装入内存(前提是:类还没有装入内存),不做类的初始化工作,返回Class的对象。
  2. Class.forName(“类名字符串”):装入类,并做类的静态初始化,返回Class的对象。
  3. 实例对象.getClass():对类进行静态初始化、非静态初始化;返回引用运行时真正所指的对象(子对象的引用会赋给父对象的引用变量中)所属的类的Class的对象。

应用场景

  1. 框架设计:在框架设计中,通常需要使用反射技术来解耦,使框架可扩展和灵活。
  2. 单元测试:在单元测试中,我们可以使用反射技术来访问私有或受保护的类成员,使测试更加全面。
  3. 动态代理:使用反射技术可以创建动态代理对象,从而可以在运行时期代理任意一个实现了接口的对象,实现AOP等功能。
  4. 序列化和反序列化:许多Java序列化和反序列化工具都是基于Java反射机制实现的,例如Java的ObjectInputStream和ObjectOutputStream。

Java反射技术可以在很多场景中应用,尤其是在框架设计和组件化开发中,反射技术可以提高代码的灵活性和可扩展性,减少代码耦合性,简化代码的编写。但是,反射机制也增加了程序的复杂度,因此必须谨慎使用。

执行流程

public class HelloReflect {public static void main(String[] args) {try {// 1. 使用外部配置的实现,进行动态加载类TempFunctionTest test = (TempFunctionTest)Class.forName("com.tester.HelloReflect").newInstance();test.sayHello("call directly");// 2. 根据配置的函数名,进行方法调用(不需要通用的接口抽象)Object t2 = new TempFunctionTest();Method method = t2.getClass().getDeclaredMethod("sayHello", String.class);method.invoke(test, "method invoke");} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchMethodException e ) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}public void sayHello(String word) {System.out.println("hello," + word);}}

反射获取类实例

获取类信息

首先调用了 java.lang.Class 的静态方法,获取类信息。

@CallerSensitive
public static Class <? > forName(String className) throws ClassNotFoundException {// 先通过反射,获取调用进来的类信息,从而获取当前的 classLoaderClass <? > caller = Reflection.getCallerClass();// 调用native方法进行获取class信息return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

forName()反射获取类信息,并没有将实现留给了java,而是交给了jvm去加载。

主要是先获取 ClassLoader, 然后调用 native 方法,获取信息,加载类则是回调 java.lang.ClassLoader.

最后,jvm又会回调 ClassLoader 进类加载。

public Class <? > loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}// sun.misc.Launcherpublic Class <? > loadClass(String var1, boolean var2) throws ClassNotFoundException {int var3 = var1.lastIndexOf(46);if (var3 != -1) {SecurityManager var4 = System.getSecurityManager();if (var4 != null) {var4.checkPackageAccess(var1.substring(0, var3));}}if (this.ucp.knownToNotExist(var1)) {Class var5 = this.findLoadedClass(var1);if (var5 != null) {if (var2) {this.resolveClass(var5);}return var5;} else {throw new ClassNotFoundException(var1);}} else {return super.loadClass(var1, var2);}}// java.lang.ClassLoaderprotected Class <? > loadClass(String name, boolean resolve)throws ClassNotFoundException {// 先获取锁synchronized(getClassLoadingLock(name)) {// First, check if the class has already been loaded// 如果已经加载了的话,就不用再加载了Class <? > c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {// 双亲委托加载if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}// 父类没有加载到时,再自己加载if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}protected Object getClassLoadingLock(String className) {Object lock = this;if (parallelLockMap != null) {// 使用 ConcurrentHashMap来保存锁Object newLock = new Object();lock = parallelLockMap.putIfAbsent(className, newLock);if (lock == null) {lock = newLock;}}return lock;}protected final Class <? > findLoadedClass(String name) {if (!checkName(name))return null;return findLoadedClass0(name);}

newInstance() 的实现

下面来看一下 newInstance() 的实现方式。

// 首先肯定是 Class.newInstance
@CallerSensitive
public T newInstance() throws InstantiationException, IllegalAccessException {if (System.getSecurityManager() != null) {//如果存在安全管理器,则调用 checkMemberAccess 方法进行安全检查,以确保调用者具有足够的权限来创建实例。checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);}// NOTE: the following code may not be strictly correct under// the current Java memory model.// Constructor lookup//如果 cachedConstructor 为空,则查找无参构造函数。if (cachedConstructor == null) {if (this == Class.class) {// 不允许调用 Class 的 newInstance() 方法throw new IllegalAccessException("Can not call newInstance() on the Class for java.lang.Class");}try {// 获取无参构造器Class <? > [] empty = {};//getConstructor0 方法获取无参构造函数,并将其设置为可访问(setAccessible(true))。final Constructor < T > c = getConstructor0(empty, Member.DECLARED);// Disable accessibility checks on the constructor// since we have to do the security check here anyway// (the stack depth is wrong for the Constructor's// security check to work)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction < Void > () {public Void run() {c.setAccessible(true);return null;}});//将获取到的构造函数缓存起来,以便下次调用时可以直接使用。cachedConstructor = c;} catch (NoSuchMethodException e) {//如果不存在无参构造函数,那么就会抛出 InstantiationExceptionthrow (InstantiationException) new InstantiationException(getName()).initCause(e);}}//获取缓存的构造函数,并检查其修饰符。Constructor < T > tmpConstructor = cachedConstructor;// Security check (same as in java.lang.reflect.Constructor)int modifiers = tmpConstructor.getModifiers();if (!Reflection.quickCheckMemberAccess(this, modifiers)) {//如果快速检查不通过,则获取调用者类,并调用 Reflection.ensureMemberAccess 方法进行更详细的检查,并将调用者类缓存起来。Class <? > caller = Reflection.getCallerClass();if (newInstanceCallerCache != caller) {Reflection.ensureMemberAccess(caller, this, null, modifiers);newInstanceCallerCache = caller;}}// Run constructortry {// 调用无参构造器return tmpConstructor.newInstance((Object[]) null);} catch (InvocationTargetException e) {Unsafe.getUnsafe().throwException(e.getTargetException());// Not reachedreturn null;}
}

newInstance() 主要做了三件事:

  1. 权限检测,如果不通过直接抛出异常;
  2. 查找无参构造器,并将其缓存起来;
  3. 调用具体方法的无参构造方法,生成实例并返回;

获取构造器

下面是获取构造器的过程:

 private Constructor < T > getConstructor0(Class <? > [] parameterTypes, int which) throws NoSuchMethodException {// 获取所有构造器Constructor < T > [] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));for (Constructor < T > constructor: constructors) {if (arrayContentsEq(parameterTypes,constructor.getParameterTypes())) {return getReflectionFactory().copyConstructor(constructor);}}throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));}

getConstructor0() 为获取匹配的构造方器;分三步:

  1. 先获取所有的constructors,然后通过进行参数类型比较;
  2. 找到匹配后,通过 ReflectionFactory copy一份constructor返回;
  3. 否则抛出 NoSuchMethodException;
// 获取当前类所有的构造方法,通过jvm或者缓存
// Returns an array of "root" constructors. These Constructor
// objects must NOT be propagated to the outside world, but must
// instead be copied via ReflectionFactory.copyConstructor.
private Constructor < T > [] privateGetDeclaredConstructors(boolean publicOnly) {checkInitted();Constructor < T > [] res;// 调用 reflectionData(), 获取保存的信息,使用软引用保存,从而使内存不够可以回收ReflectionData < T > rd = reflectionData();if (rd != null) {res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;// 存在缓存,则直接返回if (res != null) return res;}// No cached value available; request value from VMif (isInterface()) {@SuppressWarnings("unchecked")Constructor < T > [] temporaryRes = (Constructor < T > []) new Constructor <? > [0];res = temporaryRes;} else {// 使用native方法从jvm获取构造器res = getDeclaredConstructors0(publicOnly);}if (rd != null) {// 最后,将从jvm中读取的内容,存入缓存if (publicOnly) {rd.publicConstructors = res;} else {rd.declaredConstructors = res;}}return res;
}// 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 instancereturn newReflectionData(reflectionData, classRedefinedCount);
}// 新创建缓存,保存反射信息
private ReflectionData < T > newReflectionData(SoftReference < ReflectionData < T >> oldReflectionData,int classRedefinedCount) {if (!useCaches) return null;// 使用cas保证更新的线程安全性,所以反射是保证线程安全的while (true) {ReflectionData < T > rd = new ReflectionData < > (classRedefinedCount);// try to CAS it...if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference < > (rd))) {return rd;}// 先使用CAS更新,如果更新成功,则立即返回,否则测查当前已被其他线程更新的情况,如果和自己想要更新的状态一致,则也算是成功了oldReflectionData = this.reflectionData;classRedefinedCount = this.classRedefinedCount;if (oldReflectionData != null &&(rd = oldReflectionData.get()) != null &&rd.redefinedCount == classRedefinedCount) {return rd;}}
}

如上,privateGetDeclaredConstructors(), 获取所有的构造器主要步骤;

  1. 先尝试从缓存中获取;
  2. 如果缓存没有,则从jvm中重新获取,并存入缓存,缓存使用软引用进行保存,保证内存可用;

另外,使用 relactionData() 进行缓存保存;ReflectionData 的数据结构如下。

// reflection data that might get invalidated when JVM TI RedefineClasses() is called
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 getMethodsvolatile Field[] declaredPublicFields;volatile Method[] declaredPublicMethods;volatile Class <? > [] interfaces;// Value of classRedefinedCount when we created this ReflectionData instancefinal int redefinedCount;ReflectionData(int redefinedCount) {this.redefinedCount = redefinedCount;}
}

其中,还有一个点,就是如何比较构造是否是要查找构造器,其实就是比较类型完成相等就完了,有一个不相等则返回false。

private static boolean arrayContentsEq(Object[] a1, Object[] a2) {if (a1 == null) {return a2 == null || a2.length == 0;}if (a2 == null) {return a1.length == 0;}if (a1.length != a2.length) {return false;}for (int i = 0; i < a1.length; i++) {if (a1[i] != a2[i]) {return false;}}return true;}// sun.reflect.ReflectionFactory/** Makes a copy of the passed constructor. The returnedconstructor is a "child" of the passed one; see the commentsin Constructor.java for details. */
public < T > Constructor < T > copyConstructor(Constructor < T > arg) {return langReflectAccess().copyConstructor(arg);
}// java.lang.reflect.Constructor, copy 其实就是新new一个 Constructor 出来
Constructor < T > copy() {// This routine enables sharing of ConstructorAccessor objects// among Constructor objects which refer to the same underlying// method in the VM. (All of this contortion is only necessary// because of the "accessibility" bit in AccessibleObject,// which implicitly requires that new java.lang.reflect// objects be fabricated for each reflective call on Class// objects.)if (this.root != null)throw new IllegalArgumentException("Can not copy a non-root Constructor");Constructor < T > res = new Constructor < > (clazz,parameterTypes,exceptionTypes, modifiers, slot,signature,annotations,parameterAnnotations);// root 指向当前 constructorres.root = this;// Might as well eagerly propagate this if already presentres.constructorAccessor = constructorAccessor;return res;
}

通过上面,获取到 Constructor 了。

接下来就只需调用其相应构造器的 newInstance(),即返回实例了。

// return tmpConstructor.newInstance((Object[])null); 
// java.lang.reflect.Constructor
@CallerSensitive
public T newInstance(Object...initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {if (!override) {if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {Class <? > caller = Reflection.getCallerClass();checkAccess(caller, clazz, null, modifiers);}}if ((clazz.getModifiers() & Modifier.ENUM) != 0)throw new IllegalArgumentException("Cannot reflectively create enum objects");ConstructorAccessor ca = constructorAccessor; // read volatileif (ca == null) {ca = acquireConstructorAccessor();}@SuppressWarnings("unchecked")T inst = (T) ca.newInstance(initargs);return inst;
}
// sun.reflect.DelegatingConstructorAccessorImpl
public Object newInstance(Object[] args) throws InstantiationException, IllegalArgumentException, InvocationTargetException {return delegate.newInstance(args);
}
// sun.reflect.NativeConstructorAccessorImpl
public Object newInstance(Object[] args) throws InstantiationException, IllegalArgumentException, InvocationTargetException {// We can't inflate a constructor belonging to a vm-anonymous class// because that kind of class can't be referred to by name, hence can't// be found from the generated bytecode.if (++numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {ConstructorAccessorImpl acc = (ConstructorAccessorImpl)new MethodAccessorGenerator().generateConstructor(c.getDeclaringClass(),c.getParameterTypes(),c.getExceptionTypes(),c.getModifiers());parent.setDelegate(acc);}// 调用native方法,进行调用 constructorreturn newInstance0(c, args);
}

返回构造器的实例后,可以根据外部进行进行类型转换,从而使用接口或方法进行调用实例功能了。

获取类实例两种方式的区别

  • Class.newInstance():只能够调用无参的构造函数,即默认的构造函数;并且要求被调用的构造函数是可见的,也即必须是public类型的;
  • Constructor.newInstance():可以根据传入的参数,调用任意构造构造函数;可以调用私有的构造函数。

在JDK9之后 class.newInstance()方法就被弃用了,详情可以看这篇文章 弃用class-newinstance

反射获取方法

获取 Method

// java.lang.Class
@CallerSensitive
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;
}

忽略第一个检查权限,剩下就只有两个动作了。

  • 获取所有方法列表;

  • 根据方法名称和方法列表,选出符合要求的方法;

  • 如果没有找到相应方法,抛出异常,否则返回对应方法;

所以,先看一下怎样获取类声明的所有方法

// Returns an array of "root" methods. These Method objects must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyMethod.
private Method[] privateGetDeclaredMethods(boolean publicOnly) {checkInitted();Method[] res;ReflectionData < T > rd = reflectionData();if (rd != null) {res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;if (res != null) return res;}// No cached value available; request value from VMres = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));if (rd != null) {if (publicOnly) {rd.declaredPublicMethods = res;} else {rd.declaredMethods = res;}}return res;
}

很相似,和获取所有构造器的方法很相似,都是先从缓存中获取方法,如果没有,则从jvm中获取。

不同的是,方法列表需要进行过滤 Reflection.filterMethods;当然后面看来,这个方法我们一般不会派上用场。

// sun.misc.Reflection
public static Method[] filterMethods(Class <? > containingClass, Method[] methods) {if (methodFilterMap == null) {// Bootstrappingreturn methods;}return (Method[]) filter(methods, methodFilterMap.get(containingClass));
}// 可以过滤指定的方法,一般为空,如果要指定过滤,可以调用 registerMethodsToFilter(), 或者...
private static Member[] filter(Member[] members, String[] filteredNames) {if ((filteredNames == null) || (members.length == 0)) {return members;}int numNewMembers = 0;for (Member member: members) {boolean shouldSkip = false;for (String filteredName: filteredNames) {if (member.getName() == filteredName) {shouldSkip = true;break;}}if (!shouldSkip) {++numNewMembers;}}Member[] newMembers =(Member[]) Array.newInstance(members[0].getClass(), numNewMembers);int destIdx = 0;for (Member member: members) {boolean shouldSkip = false;for (String filteredName: filteredNames) {if (member.getName() == filteredName) {shouldSkip = true;break;}}if (!shouldSkip) {newMembers[destIdx++] = member;}}return newMembers;
}

根据方法名和参数类型过滤指定方法返回

private static Method searchMethods(Method[] methods, String name, Class <? > [] parameterTypes) {Method res = null;// 使用常量池,避免重复创建StringString 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));
}

大概意思看得明白,就是匹配到方法名,然后参数类型匹配,才可以。

  • 但是可以看到,匹配到一个方法,并没有退出for循环,而是继续进行匹配。

  • 这里是匹配最精确的子类进行返回(最优匹配)

  • 最后,还是通过 ReflectionFactory, copy 方法后返回。

调用 method.invoke() 方法

@CallerSensitive
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 volatileif (ma == null) {ma = acquireMethodAccessor();}return ma.invoke(obj, args);
}

invoke时,是通过 MethodAccessor 进行调用的,而 MethodAccessor 是个接口,在第一次时调用 acquireMethodAccessor() 进行新创建。

// probably make the implementation more scalable.
private MethodAccessor acquireMethodAccessor() {// First check to see if one has been created yet, and take it// if soMethodAccessor tmp = null;if (root != null) tmp = root.getMethodAccessor();if (tmp != null) {// 存在缓存时,存入 methodAccessor,否则调用 ReflectionFactory 创建新的 MethodAccessormethodAccessor = tmp;} else {// Otherwise fabricate one and propagate it up to the roottmp = reflectionFactory.newMethodAccessor(this);setMethodAccessor(tmp);}return tmp;}// sun.reflect.ReflectionFactory
public MethodAccessor newMethodAccessor(Method method) {checkInitted();if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {return new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),method.getName(),method.getParameterTypes(),method.getReturnType(),method.getExceptionTypes(),method.getModifiers());} else {NativeMethodAccessorImpl acc =new NativeMethodAccessorImpl(method);DelegatingMethodAccessorImpl res =new DelegatingMethodAccessorImpl(acc);acc.setParent(res);return res;}
}

两个Accessor详情:

//NativeMethodAccessorImpl / DelegatingMethodAccessorImpl
class NativeMethodAccessorImpl extends MethodAccessorImpl {private final Method method;private DelegatingMethodAccessorImpl parent;private int numInvocations;NativeMethodAccessorImpl(Method method) {this.method = method;}public Object invoke(Object obj, Object[] args)throws IllegalArgumentException, InvocationTargetException{// We can't inflate methods belonging to vm-anonymous classes because// that kind of class can't be referred to by name, hence can't be// found from the generated bytecode.if (++numInvocations > ReflectionFactory.inflationThreshold()&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {MethodAccessorImpl acc = (MethodAccessorImpl)new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),method.getName(),method.getParameterTypes(),method.getReturnType(),method.getExceptionTypes(),method.getModifiers());parent.setDelegate(acc);}return invoke0(method, obj, args);}void setParent(DelegatingMethodAccessorImpl parent) {this.parent = parent;}private static native Object invoke0(Method m, Object obj, Object[] args);
}
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {private MethodAccessorImpl delegate;DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {setDelegate(delegate);}public Object invoke(Object obj, Object[] args)throws IllegalArgumentException, InvocationTargetException{return delegate.invoke(obj, args);}void setDelegate(MethodAccessorImpl delegate) {this.delegate = delegate;}
}

进行 ma.invoke(obj, args); 调用时,调用 DelegatingMethodAccessorImpl.invoke();

最后被委托到 NativeMethodAccessorImpl.invoke(), 即:

public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException {// We can't inflate methods belonging to vm-anonymous classes because// that kind of class can't be referred to by name, hence can't be// found from the generated bytecode.if (++numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {MethodAccessorImpl acc = (MethodAccessorImpl)new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),method.getName(),method.getParameterTypes(),method.getReturnType(),method.getExceptionTypes(),method.getModifiers());parent.setDelegate(acc);}// invoke0 是个 native 方法,由jvm进行调用业务方法。从而完成反射调用功能。return invoke0(method, obj, args);
}

其中, generateMethod() 是生成具体类的方法:

/** This routine is not thread-safe */
public MethodAccessor generateMethod(Class <? > declaringClass,String name,Class <? > [] parameterTypes,Class <? > returnType,Class <? > [] checkedExceptions,int modifiers) {return (MethodAccessor) generate(declaringClass,name,parameterTypes,returnType,checkedExceptions,modifiers,false,false,null);
}

generate() 戳详情。

 /** This routine is not thread-safe */private MagicAccessorImpl generate(final Class <? > declaringClass,String name,Class <? > [] parameterTypes,Class <? > returnType,Class <? > [] checkedExceptions,int modifiers,boolean isConstructor,boolean forSerialization,Class <? > serializationTargetClass) {ByteVector vec = ByteVectorFactory.create();asm = new ClassFileAssembler(vec);this.declaringClass = declaringClass;this.parameterTypes = parameterTypes;this.returnType = returnType;this.modifiers = modifiers;this.isConstructor = isConstructor;this.forSerialization = forSerialization;asm.emitMagicAndVersion();// Constant pool entries:// ( * = Boxing information: optional)// (+  = Shared entries provided by AccessorGenerator)// (^  = Only present if generating SerializationConstructorAccessor)//     [UTF-8] [This class's name]//     [CONSTANT_Class_info] for above//     [UTF-8] "sun/reflect/{MethodAccessorImpl,ConstructorAccessorImpl,SerializationConstructorAccessorImpl}"//     [CONSTANT_Class_info] for above//     [UTF-8] [Target class's name]//     [CONSTANT_Class_info] for above// ^   [UTF-8] [Serialization: Class's name in which to invoke constructor]// ^   [CONSTANT_Class_info] for above//     [UTF-8] target method or constructor name//     [UTF-8] target method or constructor signature//     [CONSTANT_NameAndType_info] for above//     [CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info] for target method//     [UTF-8] "invoke" or "newInstance"//     [UTF-8] invoke or newInstance descriptor//     [UTF-8] descriptor for type of non-primitive parameter 1//     [CONSTANT_Class_info] for type of non-primitive parameter 1//     ...//     [UTF-8] descriptor for type of non-primitive parameter n//     [CONSTANT_Class_info] for type of non-primitive parameter n// +   [UTF-8] "java/lang/Exception"// +   [CONSTANT_Class_info] for above// +   [UTF-8] "java/lang/ClassCastException"// +   [CONSTANT_Class_info] for above// +   [UTF-8] "java/lang/NullPointerException"// +   [CONSTANT_Class_info] for above// +   [UTF-8] "java/lang/IllegalArgumentException"// +   [CONSTANT_Class_info] for above// +   [UTF-8] "java/lang/InvocationTargetException"// +   [CONSTANT_Class_info] for above// +   [UTF-8] "<init>"// +   [UTF-8] "()V"// +   [CONSTANT_NameAndType_info] for above// +   [CONSTANT_Methodref_info] for NullPointerException's constructor// +   [CONSTANT_Methodref_info] for IllegalArgumentException's constructor// +   [UTF-8] "(Ljava/lang/String;)V"// +   [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/String;)V"// +   [CONSTANT_Methodref_info] for IllegalArgumentException's constructor taking a String// +   [UTF-8] "(Ljava/lang/Throwable;)V"// +   [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/Throwable;)V"// +   [CONSTANT_Methodref_info] for InvocationTargetException's constructor// +   [CONSTANT_Methodref_info] for "super()"// +   [UTF-8] "java/lang/Object"// +   [CONSTANT_Class_info] for above// +   [UTF-8] "toString"// +   [UTF-8] "()Ljava/lang/String;"// +   [CONSTANT_NameAndType_info] for "toString()Ljava/lang/String;"// +   [CONSTANT_Methodref_info] for Object's toString method// +   [UTF-8] "Code"// +   [UTF-8] "Exceptions"//  *  [UTF-8] "java/lang/Boolean"//  *  [CONSTANT_Class_info] for above//  *  [UTF-8] "(Z)V"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "booleanValue"//  *  [UTF-8] "()Z"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "java/lang/Byte"//  *  [CONSTANT_Class_info] for above//  *  [UTF-8] "(B)V"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "byteValue"//  *  [UTF-8] "()B"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "java/lang/Character"//  *  [CONSTANT_Class_info] for above//  *  [UTF-8] "(C)V"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "charValue"//  *  [UTF-8] "()C"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "java/lang/Double"//  *  [CONSTANT_Class_info] for above//  *  [UTF-8] "(D)V"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "doubleValue"//  *  [UTF-8] "()D"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "java/lang/Float"//  *  [CONSTANT_Class_info] for above//  *  [UTF-8] "(F)V"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "floatValue"//  *  [UTF-8] "()F"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "java/lang/Integer"//  *  [CONSTANT_Class_info] for above//  *  [UTF-8] "(I)V"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "intValue"//  *  [UTF-8] "()I"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "java/lang/Long"//  *  [CONSTANT_Class_info] for above//  *  [UTF-8] "(J)V"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "longValue"//  *  [UTF-8] "()J"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "java/lang/Short"//  *  [CONSTANT_Class_info] for above//  *  [UTF-8] "(S)V"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for above//  *  [UTF-8] "shortValue"//  *  [UTF-8] "()S"//  *  [CONSTANT_NameAndType_info] for above//  *  [CONSTANT_Methodref_info] for aboveshort numCPEntries = NUM_BASE_CPOOL_ENTRIES + NUM_COMMON_CPOOL_ENTRIES;boolean usesPrimitives = usesPrimitiveTypes();if (usesPrimitives) {numCPEntries += NUM_BOXING_CPOOL_ENTRIES;}if (forSerialization) {numCPEntries += NUM_SERIALIZATION_CPOOL_ENTRIES;}// Add in variable-length number of entries to be able to describe// non-primitive parameter types and checked exceptions.numCPEntries += (short)(2 * numNonPrimitiveParameterTypes());asm.emitShort(add(numCPEntries, S1));final String generatedName = generateName(isConstructor, forSerialization);asm.emitConstantPoolUTF8(generatedName);asm.emitConstantPoolClass(asm.cpi());thisClass = asm.cpi();if (isConstructor) {if (forSerialization) {asm.emitConstantPoolUTF8("sun/reflect/SerializationConstructorAccessorImpl");} else {asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");}} else {asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");}asm.emitConstantPoolClass(asm.cpi());superClass = asm.cpi();asm.emitConstantPoolUTF8(getClassName(declaringClass, false));asm.emitConstantPoolClass(asm.cpi());targetClass = asm.cpi();short serializationTargetClassIdx = (short) 0;if (forSerialization) {asm.emitConstantPoolUTF8(getClassName(serializationTargetClass, false));asm.emitConstantPoolClass(asm.cpi());serializationTargetClassIdx = asm.cpi();}asm.emitConstantPoolUTF8(name);asm.emitConstantPoolUTF8(buildInternalSignature());asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());if (isInterface()) {asm.emitConstantPoolInterfaceMethodref(targetClass, asm.cpi());} else {if (forSerialization) {asm.emitConstantPoolMethodref(serializationTargetClassIdx, asm.cpi());} else {asm.emitConstantPoolMethodref(targetClass, asm.cpi());}}targetMethodRef = asm.cpi();if (isConstructor) {asm.emitConstantPoolUTF8("newInstance");} else {asm.emitConstantPoolUTF8("invoke");}invokeIdx = asm.cpi();if (isConstructor) {asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");} else {asm.emitConstantPoolUTF8("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");}invokeDescriptorIdx = asm.cpi();// Output class information for non-primitive parameter typesnonPrimitiveParametersBaseIdx = add(asm.cpi(), S2);for (int i = 0; i < parameterTypes.length; i++) {Class <? > c = parameterTypes[i];if (!isPrimitive(c)) {asm.emitConstantPoolUTF8(getClassName(c, false));asm.emitConstantPoolClass(asm.cpi());}}// Entries common to FieldAccessor, MethodAccessor and ConstructorAccessoremitCommonConstantPoolEntries();// Boxing entriesif (usesPrimitives) {emitBoxingContantPoolEntries();}if (asm.cpi() != numCPEntries) {throw new InternalError("Adjust this code (cpi = " + asm.cpi() +", numCPEntries = " + numCPEntries + ")");}// Access flagsasm.emitShort(ACC_PUBLIC);// This classasm.emitShort(thisClass);// Superclassasm.emitShort(superClass);// Interfaces count and interfacesasm.emitShort(S0);// Fields count and fieldsasm.emitShort(S0);// Methods count and methodsasm.emitShort(NUM_METHODS);emitConstructor();emitInvoke();// Additional attributes (none)asm.emitShort(S0);// Load classvec.trim();final byte[] bytes = vec.getData();// Note: the class loader is the only thing that really matters// here -- it's important to get the generated code into the// same namespace as the target class. Since the generated code// is privileged anyway, the protection domain probably doesn't// matter.return AccessController.doPrivileged(new PrivilegedAction < MagicAccessorImpl > () {public MagicAccessorImpl run() {try {return (MagicAccessorImpl)ClassDefiner.defineClass(generatedName,bytes,0,bytes.length,declaringClass.getClassLoader()).newInstance();} catch (InstantiationException | IllegalAccessException e) {throw new InternalError(e);}}});}

主要看这一句:ClassDefiner.defineClass(xx, declaringClass.getClassLoader()).newInstance();

在ClassDefiner.defineClass方法实现中,每被调用一次都会生成一个DelegatingClassLoader类加载器对象 ,这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载。

而反射生成的类,有时候可能用了就可以卸载了,所以使用其独立的类加载器,从而使得更容易控制反射类的生命周期。

反射调用流程小结

  1. 反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;
  2. 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
  3. 反射也是考虑了线程安全的,放心使用;
  4. 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;
  5. 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;
  6. 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
  7. 调度反射方法,最终是由jvm执行invoke0()执行;

关于作者

来自一线程序员Seven的探索与实践,持续学习迭代中~

本文已收录于我的个人博客:https://www.seven97.top

公众号:seven97,欢迎关注~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/789338.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

django timezone.now 小了8小时

django.util.timezone.now()原因:setting.py中设置了时区:LANGUAGE_CODE = en-usTIME_ZONE = UTCUSE_I18N = TrueUSE_TZ = True # 若数据库中存储的是UTC时间,但在模板显示的时候,会转成TIME_ZONE所示的本地时间进行显示****将TIME_ZONE时区改为:TIME_ZONE = Asia/Shangha…

枚举实现原理

枚举的定义 在JDK1.5之前,我们要是想定义一些有关常量的内容,例如定义几个常量,表示从周一到周末,一般都是在一个类,或者一个接口中,写类似于如下代码: public class WeekDayConstant {public static final int MONDAY = 0;public static final int TUESDAY = 1;public …

航图中的扇区数据生成

今天简单聊一下机场扇区,最后再介绍一下《风标设计2025》航图模块中的扇区绘制功能。机场扇区是以机场基准点(ARP)或归航台为圆心,半径46km(25nm),外加9km(5nm)缓冲区构成。PBN程序扇区通常以ARP为圆心,传统程序扇区以导航台为圆心。对于中小机场,为了统一和简化扇区划设…

【Azure Policy】添加策略用于审计Azure 网络安全组(NSG)规则 -- 只能特定的IP地址允许3389/22端口访问

问题描述 对Azure上的虚拟机资源,需要进行安全管理。只有指定的IP地址才能够通过RDP/SSH远程到虚拟机上, 有如下几点考虑: 1) 使用Azure Policy服务,扫描订阅中全部的网络安全组(NSG: Network Security Group) 资源 2) 判断入站规则,判断是否是3389, 22端口 3) 判断源地…

动态规划——数字三角形模型

数字三角形模型 母题 : 数字三角形思路 ​ 集合 f [i] [j] 表示所有从起点走到(i,j)的路径 ​ 属性 f [i] [j] 存的数是集合中所有路径和的最大值 ​ 状态计算:对于每一条从起点到 ( i , j ) 的路径,其要么是从左上方来的,要么是从右上方来的。 #include<bits/stdc++…

盘点一个Pycharm汉化过程中的一个坑

大家好,我是Python进阶者。 一、前言 前几天在Python最强白银交流群【生产队的驴都是不歇的】问了一个Python汉化的问题,问题如下:要把我弄疯了,汉化不成功,想重新安装,又不给出卸载选项,通过硬盘安装插件又提示异常。去插件文件夹删除所有插件文件,再重新安装pycharm,…

深度解析:OCR技术在企业HR管理中的应用

HR管理的挑战 企业的HR部门在处理员工入职、资料审核、信息录入存储管理等环节面临着大量重复性劳动力的问题。而HR部门通常人手有限,传统的人工录入方式不仅效率低下,而且容易出错。OCR技术的引入,极大地起到了降本增效地目的。应用的办公场景人脸比对技术 应用背景:在员工…

.NET 摄像头采集

本文主要介绍摄像头(相机)如何采集数据,用于类似摄像头本地显示软件,以及流媒体数据传输场景如传屏、视讯会议等。 摄像头采集有多种方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),网上一些文章以及github已经有很多介绍,这…

Python深度学习股价预测、量化交易策略:LSTM、GRU深度门控循环神经网络|附代码数据

全文链接:https://tecdat.cn/?p=37539 原文出处:拓端数据部落公众号 分析师:Shuo Zhang 本文以上证综指近 22 年的日交易数据为样本,构建深度门控循环神经网络模型,从股价预测和制定交易策略两方面入手,量化循环神经网络在股票预测以及交易策略中的效果,结合一个Pytho…

Windos NTP 服务设定

1、修改注册表2, 双击 打开 设为自动 应用 保存

【专题】中国游戏产业AIGC发展前景报告合集PDF分享(附原数据表)

原文链接:https://tecdat.cn/?p=37535 原文出处:拓端数据部落公众号 近八成头部游戏企业在人工智能、数字孪生、引擎开发、云技术和XR等技术领域布局;有近六成头部游戏企业已构建AI生产管线、赋能虚拟内容生产或智能营销;此外,国内TOP50游戏厂商投资AI企业已超百次。阅读原…