深入理解Java动态代理机制:运行机制、代码分析与代理模式分类
基础:需要具备面向对象设计思想,多态的思想,反射的思想;

Java 动态代理机制出现了,这让 Java 开发人员无需手工编写代理类。他们只需简单地指定一组接口以及委托类对象,就能够动态地获得代理类。代理类会把所有的方法调用分派到委托对象上并进行反射执行。在这个分派执行的过程中,开发人员可以根据需要调整委托类对象及其功能。这是一个非常灵活且有弹性的代理框架。读者阅读本文后,会对 Java 动态代理机制有更深入的理解。首先,本文从 Java 动态代理的运行机制和特点开始,接着对其代码进行了分析,并且推演了动态生成类的内部实现。
代理模式的基本概念和分类
代理模式的作用是为其他对象提供一个代理,以实现对该对象的访问控制。代理对象起到中介的作用,它可以去除某些服务,或者添加额外的服务。引用他人的话来说就是:代理类负责为委托类预处理消息,对消息进行过滤并转发,同时还会在委托类执行消息后进行后续处理。
代理模式在开发中的应用场景
远程代理:为不同地理的对象提供局域网代表对象。
虚拟代理会根据需求对资源消耗很大的对象进行延迟处理,等到真正需要的时候才去创建。例如在网页中,会先显示文字,之后再显示图片。
保护代理的作用是控制不同用户的访问权限。例如:只有在客户注册成功之后,才能够进行增删改查等操作。
智能引用代理:提供对目标代理额外的服务。
代理模式的实现方式
使用继承和聚合实现动态代理,哪种更好呢!
public interface Moveable {
public void move();
}
类 Car 实现了 Moveable 接口。
@Override
public void move() {
try {
通过 new Random().nextint(1000) 来确定休眠时间,然后让线程进行休眠。
System.out 输出了“……行驶中……”。
}
捕获(InterruptedException e)。
以下是改写后的句子:在处理异常的过程中,自动生成了一个捕获块。它用于捕获可能出现的异常情况,以确保程序的稳定性和可靠性。
e.printStackTrace();
}
}
}
公共类 Car2 继承自 Car 类。
@Override
public void move()
{
//分离代码,增加业务逻辑
系统当前时间的毫秒值被获取并赋值给了一个名为 startTime 的长整型变量,这个变量被初始化为一个较大的时间值。
“汽车开始行驶……”被 System.out 打印了出来。
super.move();
System.currentTimeMillis() 的值被赋给了 long 类型的变量 endTime ,这个 endTime 是一个较长的时间值 。
}
}
继承方式实现代理
Moveablecar2=newCar2();
car2.move();
聚合方式实现代理
Carcar=newCar();
Moveablem=newCar3(car);
m.move();
总结
使用继承方式存在不够灵活的情况。在功能叠加时,只能通过臃肿地扩展代理类来实现。
使用聚合的方式,代理之间可以相互传递,灵活的组合代理;
公共类 CarLogProxy 继承自 Car 类。
@Override
public void move()
{
//分离代码,增加业务逻辑
long startTime=System.currentTimeMillis();
System.out.println("日志开始……");
super.move();
long endTime=System.currentTimeMillis();
System.out.println("日志结束……");
}
}
公共类 CarTimeProxy 实现了 Moveable 接口。
public CarTimeProxy(Car car)
{
super();
this.car=car;
}
private Carcar;
@Override
public void move() {
//分离代码,增加业务逻辑
long startTime=System.currentTimeMillis();
System.out.println("汽车开始行驶……");
car.move();
long endTime=System.currentTimeMillis();
System.out.println("汽车结束行驶……时间:"+(endTime-startTime)+"ms");
}
}
@Test:
Car car =new Car();
创建一个 CarTimeProxy 实例 ctp,这个实例是通过传入一个 car 对象来创建的。
创建一个 CarLogProxy 实例,将 ctp 作为参数传递给它,然后将这个实例赋值给 clp,即 clp 等于新创建的 CarLogProxy 对象,该对象是基于 ctp 创建的。
clp.move();
//还可以通过接口相互传递代理实例
创建了一个名为 CarLogProxy 的实例 clp1,这个实例是通过传入一个名为 car 的对象来创建的。
创建一个名为 CarTimeProxy 的对象 ctp1,该对象通过参数 clp1 进行初始化。
ctp1.move();
JDK动态代理和CGlib动态代理
JDK动态代理
代理实现
如果不同的对象要实现相同功能的代理类,应该如何处置?
此时可以尝试把它集成到同一个代理类里,也就是动态代理。动态代理能够实现对不同类以及不同方法的代理。
大致过程如下:
Java 的动态代理类处在 java.lang.reflect 包之中,通常主要会涉及到以下两个类:
InterfaceInvocationHandler 这个接口中仅仅定义了一个方法,该方法为 Publicobjectinvoke(Objectobj,Methodmethod,Object[]args) 。
obj:一般是指代理类
method:是被代理的方法
args为该方法的参数数组。
这个抽象的方法在代理类中动态实现。
(2)Proxy:该类即为动态代理类
创建一个代理实例,使用指定的类加载器、接口数组和调用处理器。具体来说,通过调用 statixObject 的 newProxyInstance 方法,传入类加载器、接口数组以及调用处理器,从而创建出一个代理对象,该代理对象可以在调用目标对象的方法时进行拦截和处理。
返回一个甙类的实例,返回后的代理类能够当作被代理类来使用,并且可以使用被代理类在接口中声明过的方法。
实现实例:
@ TimeHandler
公共类 TimeHandler 实现了 InvocationHandler 接口。
构造函数 TimeHandler 接收一个对象参数 target 。
super();
this.target = target;
}
private Objecttarget;
@Override
该方法接收三个参数,分别是代理对象 proxy、方法 method 以及参数数组 args,然后返回一个 Object 对象。
throws Throwable {
long startTime=System.currentTimeMillis();
System.out.println("汽车开始行驶……");
method.invoke(target);
long endTime=System.currentTimeMillis();
System.out.println("汽车结束行驶……时间:"+(endTime-startTime)+"ms");
return null;
}
}
@被代理类的接口
public interface Moveable {
public void move();
}
@被代理的类
public class Car implements Moveable{
@Override
public void move() {
try {
Thread.sleep(new Random().nextint(1000));
System.out.println("……行驶中……");
}
捕获到(InterruptedException 异常 e)。
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@测试
public class Test {
公共静态 void 主函数(字符串数组参数){
Car car=new Car();
创建一个 InvocationHandler 实例 h,该实例是通过将 car 作为参数传递给 TimeHandler 类来创建的。
Class>cls=car.getClass();
创建一个可移动的对象 m,m 等于通过 (Moveable)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h) 创建的代理实例。
m.move();
}
}
&&测试结果
梳理总结
所为的DynamicProxy是这样一种class:
在运行时会生成一个 class,这个 class 要去实现一组 interface。当使用动态代理类的时候,就必须要实现 InvocationHandler 接口。
JDK动态代理的一般步骤
创建一个类,这个类要实现接口 InvocationHandler。并且这个类必须要实现 invoke()这个方法。
2.创建被代理的类以及接口
3.调用Proxy的静态方法,创建一个代理类
创建一个代理实例,需要指定类加载器(ClassLoader)、接口数组(Class[])以及调用处理器(InvocationHandler)。通过 newProxyInstance 方法来实现这一操作,该方法接收这三个参数,分别用于确定代理实例的相关特性和行为。
4.通过代理调用方法
CGlib动态代理的实现
代理实现
@引入cglib-node-2.2.jar包
@CglibProxy 拦截类实现了接口 MethodInterceptor,并且对 intercept 拦截方法进行了重写。
公共类 CglibProxy 实现了 MethodInterceptor 接口。
创建了一个名为 Enhancer 的对象,并将其赋值给 private 修饰的变量 enhancer 。
要获取类 cl 的代理对象。
{
//设置创建子类的类
enhancer.setSuperclass(cl);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
公共的对象拦截(对象 obj,方法 m,对象数组 args,方法代理 proxy)并抛出异常 Throwable
{
System.out.println("日志开始……");
//代理类调用父类的方法
proxy.invokeSuper(obj, args);
System.out.println("日志结束……");
return null;
}
}
@被代理类Train
public class Train {
public void move()
{
System 输出了“火车行驶中……”。
}
}
@测试类
public class Test {
public static void main(String[] args) {
创建了一个 CglibProxy 的实例,名为 proxy 。
通过 proxy 获取 Train 类的代理对象,将其赋值给变量 t,t 为 Train 类型的对象。
t.move();
}
}
##测试结果:
梳理总结
使用CglibProxy实现动态代理的一般步骤
创建一个类,让这个类去实现接口 MethodInterceptor ,接着对 intercept 方法进行重写。
2、创建被代理类
3、调用代理类自定义的方法,得到一个代理实例
4、通过代理实例调用被代理类的需要执行的方法
比较总结
JDK动态代理
1、只能代理实现了接口的类
2、没有实现接口的类不能实现JDK的动态代理
CGlib动态代理
1、针对类来实现代理
对执行目标类创建一个子类,利用方法拦截技术来拦截所有父类方法的调用。
模拟代理产生步骤
思路:
通过 Proxy 的 newProxyInstance 这个方式能够返回代理对象。