导航
前言1 核心——Proxy类1.1 变量/域1.1.1 proxyClassCache
1.2 方法1.2.1 newProxyInstance(核心)1.2.2 getProxyClass1.2.3 defineClass0(核心)
1.3 内部类1.3.1 KeyFactory1.3.2 ProxyClassFactory1.3.3 Key1,Key2,KeyX
2 核心——InvocationHandler接口2.1 目的2.1 原理
3 示例3.1 InvocationHandler实现类3.2 被代理的接口3.3 main方法中创建接口的代理对象3.4 Proxy中自动创建的代理类class对象(简要)
参考
前言
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,见下);二级缓存(valuesMap)的key(subkey)是将传入的接口class对象数组通过KeyFactory(见1.3.1)生成的,而二级缓存的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所需参数。这里可能有些抽象,可以看 3.4 中的示例就明白了。
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对象,并返回。之所以它可以生成代理类的class对象,也是因为在apply方法中调用了native的defineClass0方法。apply方法中包括了生成代理类class对象的一些预处理,如检查传入的class对象数组中的class对象是否被类加载器可见、是否是接口、生成新的代理类的类名proxyName等,最后调用了defineClass0方法来生成新的代理类的class对象,该class对象随后用于生成代理类实例。ProxyClassFactory的apply方法在proxyClassCache的二级缓存的value——工厂对象调用get方法时调用,表示通过该工厂对象返回代理类的class对象。
1.3.3 Key1,Key2,KeyX
目的:是proxyClassCache二级缓存的键,具体类型由KeyFactory选择性生成。原理:KeyFactory在生成Key?对象时,这个对象的实际类型由传入的接口class对象的数组大小决定,数组中大于2个class对象时生成KeyX类的对象/实例,而只有1个(最常见)或2个class对象时分别生成Key1类和Key2类的对象,这样做是因为针对后两种的情况进行了代码的优化。
2 核心——InvocationHandler接口
2.1 目的
通过InvocationHandler的实现类,实现了使得代理对象在调用接口方法时,转而执行增强的、代理版的方法——invoke(Object proxy, Method method, Object[] args)。InvocationHandler的实现类可以看做代理对象方法调用的调用处理器,或调用转发器。
2.1 原理
该接口的实现类相当于一个方法调用处理器,在Proxy.newProxyInstance调用时被传入到Proxy对象中。随后,在Proxy产生了代理类的class对象,并通过反射生成代理对象时,被传入到代理对象中。因此,代理对象调用任何方法时,都会使用invocationHandler执行定义的invoke方法。可以看到真正的增强功能的逻辑(切面)是在invoke方法中定义的。
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/52143504https://www.cnblogs.com/liuyun1995/p/8144676.htmlhttps://zhuanlan.zhihu.com/p/88759564https://segmentfault.com/a/1190000011291179