一、反射的用法

1、如何获取Class反射类

  (1)通过getClass方法:

  Proxy proxy = new ProxyImpl();
  Class proxyClazz = proxy.getClass();

  (2)通过Class.forName方法 

  Proxy proxy = new ProxyImpl();
  Class proxyClazz = Class.forName("com.dh.yjt.SpringBootDemo.test.Reflect.Proxy");

  (3)通过.class 

  Proxy proxy = new ProxyImpl();
  Class proxyClazz = Proxy.class;

2、获取类型信息

  反射的一大好处就是可以允许我们在运行期间获取对象的类型信息,例如需要再运行期间获取对象方法信息,并执行该方法,可以通过以下方式:

  首先创建一个接口及其实现类,其中实现类的中方法可见性为不同级别:

  class ProxyImpl implements Proxy{
public void run(){
System.out.println("run");
} public void publicFun(){
System.out.println("publicFun");
} private void privateFun(){
System.out.println("privateFun");
} void packageFun(){
System.out.println("packageFun");
} protected void protectedFun(){
System.out.println("protectedFun");
}
} interface Proxy{
public void run();
}

  然后可以通过反射获取对象中的方法并执行:

   @Test
public void fun() throws Exception {
Proxy proxy = new ProxyImpl();
proxy.run();
callHiddenMethod(proxy,"publicFun");
callHiddenMethod(proxy,"privateFun");
callHiddenMethod(proxy,"packageFun");
callHiddenMethod(proxy,"protectedFun");
} void callHiddenMethod(Object a, String methodName) throws Exception {
Method g = a.getClass().getDeclaredMethod(methodName);//根据名称获取声明的方法
g.setAccessible(true);//设置可见性
g.invoke(a);//执行方法,入参为该对象
}

  输出结果: 

  run
  publicFun
  privateFun
  packageFun
  protectedFun

  可以看出,通过反射可以执行对象的所有可见性的方法。

二、反射实现原理

1、获取Method对象  

  从上面的案例可以看出,调用Class类的getDeclaredMethod可以获取指定方法名和参数的方法对象Method:

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

  其中privateGetDeclaredMethods方法从缓存或JVM中获取该Class中声明的方法列表,searchMethods方法将从返回的方法列表里找到一个匹配名称和参数的方法对象。 

  private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData();//获取reflectionData,其中缓存了该类的方法、变量等信息
if (rd != null) {//如果不为空,则返回reflectionData中保存的方法列表
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
}
// No cached value available; request value from VM
     //如果缓存中不存在,则从JVM中获取
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
}
return res;
}

  其中reflectionData()方法实现如下:

private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;//获取当前Class对象的reflectionData
int classRedefinedCount = this.classRedefinedCount;
ReflectionData<T> rd;
     //如果reflectionData不为空则返回
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
     //否则创建新的reflectionData
return newReflectionData(reflectionData, classRedefinedCount);
}

  从reflectionData()方法实现可以看出:reflectionData对象是SoftReference类型的,说明在内存紧张时可能会被回收,不过也可以通过-XX:SoftRefLRUPolicyMSPerMB参数控制回收的时机,只要发生GC就会将其回收,如果reflectionData被回收之后,又执行了反射方法,那只能通过newReflectionData方法重新创建一个这样的对象了,

  private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData, int classRedefinedCount) {
if (!useCaches) return null;//不使用缓存,则返回空 while (true) {
ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
// try to CAS it...
       //通过CAS操作
if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
return rd;
}
// else retry
oldReflectionData = this.reflectionData;
classRedefinedCount = this.classRedefinedCount;
if (oldReflectionData != null &&
(rd = oldReflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
}
}

  其中ReflectionData定义如下:

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

   总结:在privateGetDeclaredMethods方法中,如果通过reflectionData()获得的ReflectionData对象不为空,则尝试从ReflectionData对象中获取declaredMethods属性,如果是第一次,或则被GC回收之后,重新初始化后的类属性为空,则需要重新到JVM中获取一次,并赋值给ReflectionData,下次调用就可以使用缓存数据了。

  然后通过searchMethods方法将从返回的方法列表里找到一个匹配名称和参数的方法对象:
  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));
}

  如果找到一个匹配的Method,则重新copy一份返回,即Method.copy()方法:

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

   防止坑1:从代码中可以看出,每次调用getDeclaredMethod方法返回的Method对象其实都是一个新的对象,且新对象的root属性都指向复制前的Method对象,如果需要频繁调用,最好把Method对象缓存起来。

2、执行method

  获取到指定的方法对象Method之后,就可以调用它的invoke方法了,invoke实现如下:

  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对象是invoke方法实现的关键,一开始methodAccessor为空,需要调用acquireMethodAccessor生成一个新的MethodAccessor对象,MethodAccessor本身就是一个接口,实现如下:

  private MethodAccessor acquireMethodAccessor() {
// First check to see if one has been created yet, and take it
// if so
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
} return tmp;
}

  在acquireMethodAccessor方法中,主要通过ReflectionFactory类的newMethodAccessor创建一个实现了MethodAccessor接口的对象,实现如下:

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

  在ReflectionFactory类中,有2个重要的字段:noInflation(默认false)和inflationThreshold(默认15),在checkInitted方法中可以通过-Dsun.reflect.inflationThreshold=xxx和-Dsun.reflect.noInflation=true对这两个字段重新设置,而且只会设置一次;

  如果noInflation为false,方法newMethodAccessor都会返回DelegatingMethodAccessorImpl对象,DelegatingMethodAccessorImpl的类实现: 

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;
}
}
  其实,DelegatingMethodAccessorImpl对象就是一个代理对象,负责调用被代理对象delegate的invoke方法,其中delegate参数目前是NativeMethodAccessorImpl对象,所以最终Method的invoke方法调用的是NativeMethodAccessorImpl对象invoke方法,实现如下:
  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);
}

  这里用到了ReflectionFactory类中的inflationThreshold,当delegate调用了15次invoke方法之后,如果继续调用就通过MethodAccessorGenerator类的generateMethod方法生成MethodAccessorImpl对象,并设置为delegate对象,这样下次执行Method.invoke时,就调用新建的MethodAccessor对象的invoke()方法了。

  防止踩坑2:如果多次执行Method的invoke方法,每超过15次就会创建一个新的MethodAccessorImpl对象,因此会导致内存增涨

参考:

  1、Java反射机制应用实践  http://www.importnew.com/24042.html

  2、深入分析Java方法反射的实现原理  https://www.jianshu.com/p/3ea4a6b57f87

  3、假笨说-从一起GC血案谈到反射原理  https://mp.weixin.qq.com/s/5H6UHcP6kvR2X5hTj_SBjA?

Java反射实现原理分析的更多相关文章

  1. Java Reference核心原理分析

    本文转载自Java Reference核心原理分析 导语 带着问题,看源码针对性会更强一点.印象会更深刻.并且效果也会更好.所以我先卖个关子,提两个问题(没准下次跳槽时就被问到). 我们可以用Byte ...

  2. ReflectASM-invoke,高效率java反射机制原理

    前言:前段时间在设计公司基于netty的易用框架时,很多地方都用到了反射机制.反射的性能一直是大家有目共睹的诟病,相比于直接调用速度上差了很多.但是在很多地方,作为未知通用判断的时候,不得不调用反射类 ...

  3. Java 线程池原理分析

    1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等 ...

  4. java string 细节原理分析(2016.5)

    看到了以前2016.5月学习java写的笔记,这里放在一起. String实现的细节原理分析 一.jdk源码中String 的实现 public final class String implemen ...

  5. Java面试题之Java反射的原理

    什么是Java的反射? 在运行状态中,对于任意一个类,都能够知道这个类的属性和方法: 反射的作用? 如果给定一个类名,就可以通过反射机制来获取类的所有信息,也可以动态的创建对象和编译: 反射的原理? ...

  6. Java程序运行原理分析

    class文件内容 class文件包含Java程序执行的字节码 数据严格按照格式紧凑排列在class文件的二进制流,中间无分割符 文件开头有一个0xcafebabe(16进制)特殊的标志 JVM运行时 ...

  7. 设计模式学习——JAVA动态代理原理分析

    一.JDK动态代理执行过程 上一篇我们讲了JDK动态代理的简单使用,今天我们就来研究一下它的原理. 首先我们回忆下上一篇的代码: public class Main { public static v ...

  8. Java 中 ConcurrentHashMap 原理分析

    一.Java并发基础 当一个对象或变量可以被多个线程共享的时候,就有可能使得程序的逻辑出现问题. 在一个对象中有一个变量i=0,有两个线程A,B都想对i加1,这个时候便有问题显现出来,关键就是对i加1 ...

  9. java线程启动原理分析

    一.前言不知道哪位古人说:人生三大境界.第一境界是:看山是山看水是水:第二境界是看山不是山看水不是水:第三境界:看山还是山看水还是水.其实我想对于任何一门技术的学习都是这样.形而上下者为之器,形而上者 ...

随机推荐

  1. R实用小技巧

    输出重定向 # 文本重定向 # cat cat("hello",file="D:/test.txt", append=T) # sink("filen ...

  2. Spring Boot Admin 的使用

    Spring Boot 版本: 1.5.20 一.Spring Boot Admin Server 1.在pom.xml中增加 <dependency> <groupId>or ...

  3. springboot Cacheable(redis),解决key乱码问题

    import org.springframework.context.annotation.Bean; import org.springframework.data.redis.core.Redis ...

  4. 深入理解 ORM框架

    1.ORM是什么? object Relational mapping 对象关系映射,是一种为了解决面向对象与关系型数据库存在不匹配现象的技术,简单说,orm通过描述对象和数据库之间映射的元数据,将程 ...

  5. python3-基础6

    函数嵌套调用: 再调用一个函数的过程中,又调用了其他的函数 函数的嵌套定义:在一个函数的内部,又定义另外一个函数,函数内部定义的变量,在外部不能被调用 名称空间:一种隔离的概念,专门用来存放名字的地方 ...

  6. 联想T470笔记本GPT改MBR分区

    联想T470笔记本GPT改MBR分区 7000多元买的,这个笔记本配置还可以,就是感觉特别卡顿,于是想重做WIN7系统,为了方便激活,想把GPT分区改成MBR分区. 进入微PE1.2,用傲梅分区助手删 ...

  7. 搭建 Jest+ Enzyme 测试环境

    1.为什么要使用单元测试工具? 因为代码之间的相互调用关系,又希望测试过程单元相互独立,又能正常运行,这就需要我们对被测函数的依赖函数和环境进行mock,在测试数据输入.测试执行和测试结果检查方面存在 ...

  8. 软件推荐----截图软件Snagit

    截图软件,比较推荐使用Snagit,功能上所有截图软件有的他都有.Snagit编辑器有一个[库]功能,可以按日期以及应用程序对截图进行分类整理,最赞的是你可以把整个库备份导出,在新的电脑上进行导入,所 ...

  9. 【Python爬虫】01:网络爬虫--规则

    Python网络爬虫与信息提取 目标:掌握定向网络数据爬取和网页解析的基本能力. the website is the API 课程分为以下部分: 1.requsets库(自动爬取HTML页面.自动网 ...

  10. 贝叶斯、朴素贝叶斯及调用spark官网 mllib NavieBayes示例

    贝叶斯法则   机器学习的任务:在给定训练数据A时,确定假设空间B中的最佳假设.   最佳假设:一种方法是把它定义为在给定数据A以及B中不同假设的先验概率的有关知识下的最可能假设   贝叶斯理论提供了 ...