前言
文章目录如下,便于快速索引
一、什么叫代理?
二、什么叫动态代理?
三、动态代理有什么优势?
四、动态代理的JDK实现原理
4.1核心类/接口
4.2 代理类$Proxy0解析
4.3 动态代理的经典使用
五、手写代码模拟JDK动态代理
六、参考资料
项目源码已经上传,欢迎点击下载~
先将自己总结的Java动态代理UML图放在前面,用相同的颜色代表同一个或本质上相同的类与方法,便于大家理解。接口UserService是我们自己定义的接口——接口中有方法execute();被代理类是实现了该接口的具体类;代理类则是存在于内存中,也实现了UserService接口的类,在内存中,该类名为$Proxy0。
下面进入正文,今天想跟大家分享一下我自己在学习Java动态代理过程中的理解与收获,最后用自己的代码模拟实现JDK的动态代理。
一、什么叫代理?
这个概念不是我想表达的重点,所以这里我引用别人一篇博文的例子:
“动态代理技术就是用来产生一个对象的代理对象的。在开发中为什么需要为一个对象产生代理对象呢?
举一个现实生活中的例子:歌星或者明星都有一个自己的经纪人,这个经纪人就是他们的代理人,当我们需要找明星表演时,不能直接找到该明星,只能是找明星的代理人。比如刘德华在现实生活中非常有名,会唱歌,会跳舞,会拍戏,刘德华在没有出名之前,我们可以直接找他唱歌,跳舞,拍戏,刘德华出名之后,他干的第一件事就是找一个经纪人,这个经纪人就是刘德华的代理人(代理),当我们需要找刘德华表演时,不能直接找到刘德华了(刘德华说,你找我代理人商谈具体事宜吧!),只能是找刘德华的代理人,因此刘德华这个代理人存在的价值就是拦截我们对刘德华的直接访问!
这个现实中的例子和我们在开发中是一样的,我们在开发中之所以要产生一个对象的代理对象,主要用于拦截对真实业务对象的访问。那么代理对象应该具有什么方法呢?代理对象应该具有和目标对象相同的方法
所以在这里明确代理对象的两个概念:
1、代理对象存在的价值主要用于拦截对真实业务对象的访问。(事务的开启与关闭)2、代理对象应该具有和目标对象(真实业务对象)相同的方法。(要求实现同一接口)
刘德华(真实业务对象)会唱歌,会跳舞,会拍戏,我们现在不能直接找他唱歌,跳舞,拍戏了,只能找他的代理人(代理对象)唱歌,跳舞,拍戏,一个人要想成为刘德华的代理人,那么他必须具有和刘德华一样的行为(会唱歌,会跳舞,会拍戏),刘德华有什么方法,他(代理人)就要有什么方法,我们找刘德华的代理人唱歌,跳舞,拍戏,但是代理人不是真的懂得唱歌,跳舞,拍戏的,真正懂得唱歌,跳舞,拍戏的是刘德华,在现实中的例子就是我们要找刘德华唱歌,跳舞,拍戏,那么只能先找他的经纪人,交钱给他的经纪人,然后经纪人再让刘德华去唱歌,跳舞,拍戏。”
二、什么叫动态代理?
代理类在程序运行时创建的代理方式被成为动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
三、动态代理有什么优势?
通过使用代理,通常有两个优点:
优点一:可以隐藏被代理类的实现;
优点二:可以实现客户与被代理类间的解耦,在不修改被代理类代码的情况下能够做一些额外的处理。
四、动态代理的JDK实现原理
在java的动态代理机制中,有两个重要的类和接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。当然,我还想带各位深入了解一下存在于JVM中神秘的动态代理类——$Proxy0。最后再给出java动态代理的经典使用流程。
4.1 核心类/接口
4.1.1 java.lang.reflect.Proxy类
Proxy类提供了用于创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
public class Proxy
extends Object
implements Serializable
(1)Proxy的主要静态变量
// 映射表:用于维护类装载器对象到其对应的代理类缓存
private static Map loaderToCache = new WeakHashMap();
// 标记:用于标记一个动态代理类正在被创建中
private static Object pendingGenerationMarker = new Object();
// 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
// 关联的调用处理器引用
protected InvocationHandler h;
(2)Proxy的构造方法
// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用
private Proxy() {}
// 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
protected Proxy(InvocationHandler h) {this.h = h;}
(3)Proxy静态方法newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
// 检查 h 不为空,否则抛异常
if (h == null) {
throw new NullPointerException();
}
// 获得与制定类装载器和一组接口相关的代理类类型对象
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, interfaces);
// 通过反射获取构造函数对象并生成代理类实例
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
SecurityManager sm = System.getSecurityManager();
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {