适合阅读者
复习代理模式或者需要加深理解的人群,
初学者建议找课听,和这个博客结合着看看.
免责声明:纯自己学习的理解,如有错误,欢迎斧正.
JDK动态代理
JDK动态代理一般实现步骤
1.创建一个目标类要实现的接口(后面涉及到要使用接口名称的地方,我统一写成接口)
interface 接口{}
2.创建一个接口实现类(要代理的目标对象所归属的类)
3.创建一个代理工厂类(提供了创建代理对象的方法的类)
3.1 创建代理工厂类
3.2 声明要代理的目标对象
private 目标类类 目标对象 = new 目标类();
3.3 提供创建代理对象的方法
public 接口 getProxyObject(){...}//这里返回值是对象多态的体现
测试是否成功
要深入了解的步骤
步骤3.3 提供创建代理对象的方法
这个方法要书写的代码如下:(主要是使用Proxy.newProxyInstance方法生成代理对象,然后返回代理对象
接口 proxyObject = (接口)Proxy.newProxyInstance(//这个方法生成的是Object对象,所以要强转
目标对象.getClass().getClassLoader(),//目标对象类加载器
目标对象.getClass().getInterfaces(),//代理类实现的接口的字节码对象
new InvocationHandler() {//代理对象的调用处理程序(这里是什么意思呢,下文详细分析)
/*
Object proxy : 代理对象。和proxyObject对象是同一个对象,在invoke方法中基本不用
Method method : 对接口中的方法进行封装的method对象
Object[] args : 调用方法的实际参数
返回值: 方法的返回值。
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("invoke方法执行了");
System.out.println("增加的代码逻辑");
//执行目标对象的方法
Object obj = method.invoke(目标对象, args);//这里就是调用目标对象的方法
return obj;
}
}
);
return proxyObject;
由newProxyInstance方法的第二个参数: 目标对象.getClass().getInterfaces(),//代理类实现的接口的字节码对象 , 可以确定: 代理类需要和目标类一样实现同一个接口
new InvocationHandler(){…}这个参数在做什么
要明白 new InvocationHandler(){…}这个参数在做什么,需要明白Proxy.newProxyInstance生成代理对象时利用的 代理对象所归属的 类的结构是什么(简而言之,需要明白代理类的构造)
代理类是在运行阶段生成的,使用工具arthas-boot.jar打印出代理类$Proxy0代码,如下:(这里代理类中删去了不相关的代码)
public final class $Proxy0
extends Proxy
implements 接口 {
private static Method m3;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
m3 = Class.forName("com.itheima.pattern.proxy.jdk_proxy.SellTickets").getMethod("接口中的抽象方法名", new Class[0]);
}
public final void 接口中的抽象方法名() {
this.h.invoke(this, m3, null);//这里我用的方法参数个数为0,故第三个值为null
}
}
//Java提供的动态代理相关类
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
new InvocationHandler(){…}理解步骤
- 通过Proxy.newProxyInstance方法在运行时动态的生成了一个代理类,他实现了 目标对象实现的接口
- 代理类通过反射接口字节码对象拿到接口中的抽象方法 所封装的方法对象
- 当调用代理对象中的同名方法时:
public final void 接口中的抽象方法名() {
this.h.invoke(this, m3, null);//这里我用的方法参数个数为0,故第三个值为null
}
可以看出,调用的就是在Proxy.newProxyInstance方法中的第三个参数(创建的InvocationHandler实现类对象)中的invoke方法,又因为传入了接口中的方法包装的对象method,并且在代理工厂类(提供了创建代理对象的方法的类)声明了目标对象,所以可以通过Object obj = method.invoke(目标对象, args);调用目标对象的方法,获得该方法返回值,接下来就不用说了.
new InvocationHandler() {//代理对象的调用处理程序(这里是什么意思呢,下文详细分析)
/*
Object proxy : 代理对象。和proxyObject对象是同一个对象,在invoke方法中基本不用
Method method : 对接口中的方法进行封装的method对象
Object[] args : 调用方法的实际参数
返回值: 方法的返回值。
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("invoke方法执行了");
System.out.println("增加的代码逻辑");
//执行目标对象的方法
Object obj = method.invoke(目标对象, args);//这里就是调用目标对象的方法
return obj;
}
}
总结原理
关键在于生成的代理类中的同名方法中调用的就是new InvocationHandler(){…}这个实现类对象的invoke方法,所以使用代理对象的同名方法时,走的逻辑是 : 我们在Proxy.newProxyInstance方法中第三个参数new InvocationHandler(){…}中实现的invoke方法的逻辑
CGLIB动态代理
CGLIB动态代理一般流程例子
//这里使用我学习时用到的例子
//火车站
public class TrainStation {
public void sell() {
System.out.println("火车站卖票");
}
}
//代理工厂
public class ProxyFactory implements MethodInterceptor {
private TrainStation target = new TrainStation();
public TrainStation getProxyObject() {
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer =new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation obj = (TrainStation) enhancer.create();
return obj;
}
/*
intercept方法参数说明:
o : 代理对象
method : 真实对象中的方法的Method实例
args : 实际参数
methodProxy :代理对象中的方法的method实例
*/
public TrainStation intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)");
TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
return result;
}
}
//测试类
public class Client {
public static void main(String[] args) {
//创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//获取代理对象
TrainStation proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
分析(仅加深使用层面的理解,原理不讨论)
使用方式和JDK动态代理大体过程我觉得极为相似,只是需要设置的参数有点不同,这里分析一下需要理解的地方,两个代理做个对比(这里仅仅讨论使用方式的区别,原理不讨论)
1.CGLIB生成的动态代理对象类是目标对象类的子类,JDK动态代理两个类的关系是实现了同一个接口
2.动态代理类JDK调用的是new InvocationHandler()的invoke方法,CGLIB调用的是实现了MethodInterceptor接口重写的intercept方法