在Spring的AOP切面编程中。动态代理是基础,也是很重要的一个点,学习并理解他成为掌握Spring框架很重要的点
如果我们有一个需求,需要展示Car类和Ship类的run方法,简单的sout,但是内容不一样。具体如下:
Car:"小汽车正在运行"
Ship:"轮船正在运行"
那么按照我们Java基础,我们需要定义一个Vehicle接口,把这两个类实现这个接口,并在接口run方法中实现具体需求,这对于一个刚接触Java的新手来说都做得到,但是我们需要更加好的方法,并且可以在实现他们run方法之前进行更多的步骤。那么这就需要动态代理。
动态代理是一种在程序运行时生成代理对象的机制,允许你在不修改源代码的情况下增强类的功能或拦截方法调用。动态代理通常用于实现面向切面编程(AOP)、日志记录、权限控制、事务管理等功能。
简单来说,就是我们可以生成一个代理对象,这个代理对象可以执行我们指定对象的方法,当然,他也是通过反射来完成的,并且动态代理有很多种类,常见的是JDK动态代理和CGLIB动态代理,这两种太深,我们还是初识,先不细讲。
简单比喻,就是你现在在美国,但是你在中国需要完成一件事,但是你现在不能回国,所以只能找一个代理人来帮你执行这件事,在Spring中也是如此,就是生成一个代理对象,来代替你要执行的对象,这个代理对象可以执行你要执行对象中所有方法,并且可以在方法实现之前可以完成一些代码逻辑,比如初始化,这在我们实际开发中还是很常见和很重要的,所以动态代理变得尤为重要,同时也是AOP编程的基础和核心
Vehicle接口java
代码解读复制代码public interface Vehicle {
void run();
}
Car类java
代码解读复制代码public class Car implements Vehicle{
@Override
public void run() {
System.out.println("小汽车在运行");
}
}
Ship类java
代码解读复制代码public class Ship implements Vehicle{
@Override
public void run() {
System.out.println("轮船在运行");
}
}
在用动态代理解决问题时,我们需要一个VehicleProxyProvider类,这个类用来提供我们最最重要的代理对象java
代码解读复制代码public class VehicleProxyProvider {
//该属性 target_vehicle 表示真正要执行的对象,实现了Vehicle接口
private Vehicle target_vehicle;
public VehicleProxyProvider(Vehicle target_vehicle) {
//初始化对象
this.target_vehicle = target_vehicle;
}
//编写一个方法,返回一个代理对象
public Vehicle getProxy(){
//1.得到类加载器
ClassLoader classLoader = target_vehicle.getClass().getClassLoader();
//2.得到代理对象实现的接口
Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();
//3.得到处理器对象
//InvocationHandler是接口类,不能被实例化,所以需要匿名对象的方法进行实例化,把内部的方法实现
InvocationHandler invocationHandler = new InvocationHandler() {
/*
* @description:
* @author: Pxoolcm
* @date: 2024/10/10 21:08
* @param: [proxy, method, args]
* proxy:表示的是代理对象,在这里指的是target_vehicle
* method:表示的是代理对象里面的方法
* args:表示的是代理对象里面的方法中需要传入的参数
* @return: java.lang.Object
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("交通工具开始运行了...");
//反射,方法可以调用对象
Object invoke = method.invoke(target_vehicle, args);
System.out.println("交通工具结束运行了...");
return invoke;
}
};
/*
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
1.ClassLoader loader 表示类加载器
2.Class<?>[] interfaces 表示该类实现的接口
3.InvocationHandler h 表示调用处理器对象,里面有一个重要的方法invoke
*/
Vehicle proxyInstance = (Vehicle) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
return proxyInstance;
}
}
其中target_vehicle是我们需要的代理对象,在构造方法前,一切都十分简单,但是最重要的是getProxy方法,其中:
用内置的Proxy.newProxyInstance()方法是动态代理的关键方法,它需要三个参数:
1.ClassLoader classLoader 类加载器,通过反射获取
2.Interface interface 代理对象所代理对象实现的接口,通过反射获取
3.InvocationHandler invocationHandler 调用处理器对象,该对象中有一个重要方法invoke()
那么我们继续深入,分析到了invoke()方法
invoke()方法中也需要三个参数,其中:1.Object proxy 这是我们创建的代理对象,在这个例子中就是target_vehicle
2.Method method 这是我们代理对象所代理对象的方法,这个不需要传,刚开始我也不懂,其实这是调用另一个invoke()的方法
3.Object[] args 这是我们刚刚method中需要传入的参数
这是最后也是最重要的方法了,他会返回一个对象其中也要传入两个参数,一个是代理对象和刚刚的args
这里用了反射的知识,不是上个标题的invoke,是java.lang.reflect里面Method的invoke方法,是利用反射实现的
那么万事俱备,只差Test!
代码解读复制代码public class TestVehicle {
@Test
public void runByProxy(){
Vehicle ship = new Ship();
VehicleProxyProvider shipProxy = new VehicleProxyProvider(ship);//得到代理对象提供者,传入要代理的对象
//proxy的编译类型是Vehicle
//proxy的运行类型是代理类型
Vehicle proxy = shipProxy.getProxy();//获取代理对象,该对象可以代理方法
proxy.run();
}
}
我们肯定需要创建对象,不然计算机怎么知道你要执行哪个对象的run()呢,这个案例中用了接口来接收
之后我们通过VehicleProxyProvider类来创建一个代理对象shipProxy
最后调用代理对象的getProxy()方法,远在美国的你就帮有人完成在这里的事情啦,而不需要你本人出马