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),
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; } }
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); }
如对本文有疑问, 点击进行留言回复!!
利用python将Mysql信息以Excel文件并作为邮件附件发送
springmvc+mybaits+mysql上传表情Incorrect string value: ‘\xF0\x9F\xA4\xB4\xF0\x9F...‘ for
SpringCloud Greenwich集成Seata1.2.0详解说明(原创by ulwfcyvi)
mybatis generator生成代码库 与指定的库不一致 为其他库的同名表
网友评论