Method reflection example
public class ReflectCase {
public static void main(String[] args) throws Exception {
Proxy target = new Proxy();
Method method = Proxy.class.getDeclaredMethod("run");
method.invoke(target);
}
static class Proxy {
public void run() {
System.out.println("run");
}
}
}
Through Java's reflection mechanism, any method of an object can be called during runtime. If a lot of calls are made in this way, will there be performance or memory hidden dangers? In order to thoroughly understand the reflection mechanism of methods, we can only start from the underlying code.
MethodGet
The calling Class
class getDeclaredMethod
can get the method object of the specified method name and parameters Method
.
getDeclaredMethod
![](http://upload-images.jianshu.io/upload_images/2184951-fdb93bf41864effd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
The method obtains the list of methods declared in privateGetDeclaredMethods
this from the cache or JVM, and the method will find a method object with matching name and parameters from the returned method list.Class
searchMethods
searchMethods
![](http://upload-images.jianshu.io/upload_images/2184951-d6bd35bbc6b30dd5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
If a match is found Method
, copy a copy and return, that is, the Method.copy()
method
![](http://upload-images.jianshu.io/upload_images/2184951-3d0dfd2cfeaf8383.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
getDeclaredMethod
The object returned each time the method is called Method
is actually a new object, and the root
properties of the new object all point to the original Method
object. If you need to call it frequently, it is best to Method
cache the object.
privateGetDeclaredMethods
Class
Get the list of methods declared in this from the cache or JVM. The implementation is as follows:
![](http://upload-images.jianshu.io/upload_images/2184951-27ab5408a276f756.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
The reflectionData()
method is implemented as follows:
![](http://upload-images.jianshu.io/upload_images/2184951-8dd5370724416895.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
There is a more important data structure here ReflectionData
, which is used to cache the following attribute data of the class read from the JVM:
![](http://upload-images.jianshu.io/upload_images/2184951-fb9362389bf2479c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
It can be seen from the reflectionData()
method implementation: the reflectionData
object is of a SoftReference
type, indicating that it may be recycled when the memory is tight, but -XX:SoftRefLRUPolicyMSPerMB
the timing of recycling can also be controlled by parameters. As long as GC occurs, it will be recycled. If it reflectionData
is recycled, the reflection is executed again. method, you can only newReflectionData
recreate such an object through the method, and the newReflectionData
method is implemented as follows:
![](http://upload-images.jianshu.io/upload_images/2184951-e225a5c87a9fe98e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
通过unsafe.compareAndSwapObject
方法重新设置reflectionData
字段;
在privateGetDeclaredMethods
方法中,如果通过reflectionData()
获得的ReflectionData
对象不为空,则尝试从ReflectionData
对象中获取declaredMethods
属性,如果是第一次,或则被GC回收之后,重新初始化后的类属性为空,则需要重新到JVM中获取一次,并赋值给ReflectionData
,下次调用就可以使用缓存数据了。
Method调用
获取到指定的方法对象Method
之后,就可以调用它的invoke
方法了,invoke
实现如下:
![](http://upload-images.jianshu.io/upload_images/2184951-abc39582b5e80ac1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
应该注意到:这里的MethodAccessor
对象是invoke
方法实现的关键,一开始methodAccessor
为空,需要调用acquireMethodAccessor
生成一个新的MethodAccessor
对象,MethodAccessor
本身就是一个接口,实现如下:
![](http://upload-images.jianshu.io/upload_images/2184951-0320a3b1b0fa09f5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在acquireMethodAccessor
方法中,会通过ReflectionFactory
类的newMethodAccessor
创建一个实现了MethodAccessor
接口的对象,实现如下:
![](http://upload-images.jianshu.io/upload_images/2184951-cc95a89261b02171.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在ReflectionFactory
类中,有2个重要的字段:noInflation
(默认false
)和inflationThreshold
(默认15),在checkInitted
方法中可以通过-Dsun.reflect.inflationThreshold=xxx
和-Dsun.reflect.noInflation=true
对这两个字段重新设置,而且只会设置一次;
如果noInflation
为false
,方法newMethodAccessor
都会返回DelegatingMethodAccessorImpl
对象,DelegatingMethodAccessorImpl
的类实现
![](http://upload-images.jianshu.io/upload_images/2184951-4e12c4b088111f47.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
其实,DelegatingMethodAccessorImpl
对象就是一个代理对象,负责调用被代理对象delegate
的invoke
方法,其中delegate
参数目前是NativeMethodAccessorImpl
对象,所以最终Method
的invoke
方法调用的是NativeMethodAccessorImpl
对象invoke
方法,实现如下:
![](http://upload-images.jianshu.io/upload_images/2184951-ebd0c029bbe607a4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这里用到了ReflectionFactory
类中的inflationThreshold
,当delegate
调用了15次invoke
方法之后,如果继续调用就通过MethodAccessorGenerator
类的generateMethod
方法生成MethodAccessorImpl
对象,并设置为delegate
对象,这样下次执行Method.invoke
时,就调用新建的MethodAccessor
对象的invoke()
方法了。
这里需要注意的是:generateMethod
方法在生成MethodAccessorImpl
对象时,会在内存中生成对应的字节码,并调用ClassDefiner.defineClass
创建对应的class对象,实现如下:
![](http://upload-images.jianshu.io/upload_images/2184951-f95ee19f3018c7c1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在ClassDefiner.defineClass
方法实现中,每被调用一次都会生成一个DelegatingClassLoader
类加载器对象
![](http://upload-images.jianshu.io/upload_images/2184951-f02067dd49e5d95c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载,从其设计来看本身就不希望这些类一直存在内存里的,在需要的时候有就行了。
资料来源:http://www.jianshu.com/p/3ea4a6b57f87