前言
Java的代理机制使得咱们能够在不修改原始的Java对象的代码的状况下,对原始的Java对象进行功能的加强。
Java的代理机制包括了静态代理和动态代理,静态代理须要咱们为每一个须要被代理的对象编写对应的代理类,当被代理对象较多时,使用静态代理就不太合适了,而动态代理使得咱们能够把须要被代理的对象做为参数,传递给用于生成代理对象的方法,并在运行时动态地建立代理对象。
动态代理又包括了使用JDK api实现的JDK代理和使用cglib包实现的cglib代理,JDK代理须要被代理对象实现了接口,经过invocationHandler调用原对象方法实现代理,而cglib不须要被代理对象实现接口,经过继承原对象并重写字节码的方式实现代理。
本文介绍了JDK代理的机制以及简单的使用过程,JDK代理的核心包括Proxy类和InvocationHandler接口。
如图为Proxy类的UML图。
1 核心——Proxy类
1.1 变量/域
1.1.1 proxyClassCache
类型:WeakCache<ClassLoader, Class<?>[], Class<?>>
目的:缓存生成的代理类的class对象的工厂对象,并经过缓存的工厂对象产生代理类的class对象。
原理:在Proxy调用newProxyInstance方法时,会首先在缓存中查找是否存在对应代理类的class对象,若找到了则能够直接利用该class对象,生成并返回新的代理对象;不然会利用ProxyClassFactory建立新的代理类的class对象(并放入缓存),生成并返回新的代理对象。proxyClassCache经过一个静态的WeakCache缓存实现,WeakCache经过嵌套的concurrentMap实现二级缓存 :一级缓存的key(key)是使用传入的ClassLoader包装而成的CacheKey(继承于WeakReference,见下);二级缓存的key(subkey)是使用传入的接口class对象数组经过KeyFactory(见1.3.1)生成的,而二级缓存(valuesMap)的value是一个实现了supplier接口(见下)的对象。该对象是一个用于产生目标代理类class对象的工厂,对该对象调用get方法来获取真正的代理类的class对象。此外,在WeakCache中使用到了弱引用类WeakReference和一个弱引用队列ReferenceQueue解决concurrentMap的垃圾回收问题。
supplier接口:泛型函数式接口,经过重写get方法,能够返回一个对象。
为何要使用WeakReference和ReferenceQueue:要解决这个问题,首先须要知道什么是强引用和弱引用。以本例来讲,当咱们直接向concurrentMap中存放一个键值对时,咱们向map中添加了三个对象的引用,键值对的entry(node),key和value,引用默认是强引用,意味着当咱们没有显式地从map中remove(key)时,这三个对象是不会被垃圾回收的。所以若是程序长时间运行而没有显式remove,map中加入了愈来愈多的对象,就可能形成性能问题。与强引用不一样的是,若是一个对象只存在弱引用,那么它在下一次垃圾回收时,就会被回收。所以,在这里使用了WeakReference弱引用包装类和ReferenceQueue弱引用队列来解决对象回收的问题。具体的作法是,将map的key包装到WeakReference中,实现key到其余对象的弱引用,可是若是只有key使用了弱引用是不够的,由于就算key被垃圾回收了,entry和value仍然没有被回收。利用ReferenceQueue能够解决这个问题,WeakReference在建立的时候须要两个参数,一个是被包装的key,另外一个则是一个指定的ReferenceQueue,key被注册到该ReferenceQueue中。当key被回收时,会向该ReferenceQueue中加入这个key。这时会产生一个看似矛盾的问题,为何想要key被回收,可是又要在它被回收的时候加入队列中?这是由于在key被回收时,咱们还须要经过它去清除map中的整个entry对象。在本例中,concurrentMap每次调用get方法时,都会执行一次清除工做,去清除存在于自身中且与ReferenceQueue中的key对应的entry,key和value,这样就能够及时清理掉一级缓存concurrentMap中过时的对象。
1.2 方法
1.2.1 newProxyInstance(核心)
目的:获取新的代理对象。
原理:根据传入的类加载器、接口class对象的数组和invocationHandler实现类,从proxyClassCache中获取一个实现了传入的接口方法的代理类的class对象,随后经过反射的方式获取class对象中的constructor,调用newInstance方法生成代理对象实例并返回。
1.2.2 getProxyClass
目的:从proxyClassCache中根据类加载器和接口class对象的数组返回代理类的class对象(比newProxyInstance少一个经过反射生成代理对象的步骤)。
1.2.3 defineClass0(核心)
目的:生成新的代理类的class对象。
原理:defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length),经过方法生成的新的代理类的class对象中,成员变量/实例域中包含了指定的接口中的全部方法的method对象和后期经过构造方法传入的invocationHandler实现类,成员方法中实现了指定的接口中的全部方法。在外部调用代理对象的方法时,这些方法内部其实是经过代理对象中的invocationHandler,转而调用invoke(Object proxy, Method method, Object[] args),其中proxy表明代理对象自身this,method是代理对象方法对应的成员变量method对象,args是method所需参数。
1.3 内部类
1.3.1 KeyFactory
目的:生成proxyClassCache中二级缓存(valuesMap)的键,该键标识了传入的一组class对象。
原理:KeyFactory是BiFunction接口的实现类(泛型函数式接口,经过重写apply方法,能够传入两个对象,运算而后返回一个结果对象),泛型参数为BiFunction<ClassLoader, Class<?>[], Object>,表示能够传入一个类加载器和一个接口class对象的数组,经过apply方法**生成一个Key?对象**(见1.3.3),并返回。KeyFactory实际上并无调用classLoader,仅仅经过class<?>[]的长度判断返回哪种Key。
1.3.2 ProxyClassFactory
目的:生成新的代理类的class对象。
原理:ProxyClassFactory是BiFunction接口的实现类,泛型参数为BiFunction<ClassLoader, Class<?>[], Class<?>>,表示能够传入一个类加载器和一个接口class对象的数组,经过apply方法生成一个代理类的class对象 ,并返回。apply方法中包括了生成代理类class对象的一些预处理,如检查传入的class对象数组中的class对象是否被类加载器可见、是不是接口、生成新的代理类的类名proxyName等,最后调用native的defineClass0方法 (见)生成新的代理类的class对象,该class对象随后用于生成代理类实例。ProxyClassFactory的apply方法在对proxyClassCache的二级缓存的value——工厂对象调用get方法时调用。
1.3.3 Key1,Key2,KeyX
目的:做为proxyClassCache二级缓存的键。
原理:KeyFactory在生成Key?对象时,这个对象的实际类型由传入的接口class对象的数组大小决定,数组中大于2个class对象时生成KeyX类的对象/实例,而只有1个(最多见)或2个class对象时分别生成Key1类和Key2类的对象,这样作是由于针对后两种的状况进行了代码的优化。键与键之间也是用hashcode来比较的。
2 核心——InvocationHandler接口
2.1 目的
实现了让代理对象在调用接口方法时,转而执行加强的、被代理过的方法——invoke(Object proxy, Method method, Object[] args)。html
2.1 原理
该接口的实现类至关于一个方法调用处理器,在Proxy.newProxyInstance调用时被传入到Proxy对象中。随后,在Proxy产生了代理类的class对象,并经过反射生成代理对象时,被传入到代理对象中。所以,代理对象调用任何方法时,都会使用invocationHandler执行定义的invoke方法。java
3 示例
3.1 InvocationHandler实现类
class InvocationHandlerImpl implements InvocationHandler {
Object targetObject;
public InvocationHandlerImpl ( Object targetObject) {
this . targetObject = targetObject;
}
@Override
public Object invoke ( Object proxy, Method method, Object[ ] args) throws Throwable {
System. out. println ( "start to do something.." ) ;
Object result = method. invoke ( targetObject, args) ;
System. out. println ( "end to do something.." ) ;
return result;
}
}
3.2 被代理的接口
interface Person {
void eat ( ) ;
void drink ( ) ;
}
3.3 main方法中建立接口的代理对象
public static void main ( String[ ] args) {
Person person = new Person ( ) {
@Override
public void eat ( ) {
System. out. println ( "eating..." ) ;
}
@Override
public void drink ( ) {
System. out. println ( "drinking..." ) ;
}
} ;
Person proxyPerson = ( Person) Proxy. newProxyInstance ( Person. class . getClassLoader ( ) , new Class [ ] {
Person. class } , new InvocationHandlerImpl ( person) ) ;
proxyPerson. eat ( ) ;
}
3.4 Proxy中自动建立的代理类class对象(简要)
public class $Proxy0 {
InvocationHandler invocationHandler;
Method originalEat = xxx;
Method originalDrink = xxx;
public $Proxy0 ( InvocationHandler invocationHandler) {
this . invocationHandler = invocationHandler;
}
public void eat ( ) throws Throwable {
invocationHandler. invoke ( this , originalEat, null) ;
}
public void drink ( ) throws Throwable {
invocationHandler. invoke ( this , originalDrink, null) ;
}
}
参考
https://blog.csdn.net/jiankunking/article/details/52143504
https://www.cnblogs.com/liuyun1995/p/8144676.html
https://zhuanlan.zhihu.com/p/88759564
https://segmentfault.com/a/1190000011291179