一、基础概念
1、代理模式
代理模式(Proxy Pattern)是一种结构型设计模式。它允许我们通过添加一个代理对象来控制对另一个对象的访问,从而实现一些额外的功能,如访问控制、日志记录、性能监控等。代理模式主要分为静态代理和动态代理两种。
2、静态代理
静态代理是指由程序员创建或工具生成代理类的源码,再编译代理类。代理类和委托类在运行前就确定了关系,且这种关系在运行期不能被改变。
优点:
可以在不修改目标对象的前提下,对目标对象的功能进行扩展。
代理类和目标对象实现相同的接口,因此具有相同的方法签名,这使得代理类可以无缝地替换目标对象。
缺点:
如果需要代理的目标类很多,那么就需要编写大量的代理类,这会导致代码冗余和难以维护。
一旦接口增加方法,目标对象与代理对象都需要进行维护。
3、动态代理
动态代理是指在程序运行期间,根据反射等机制动态地生成代理类的源码,并加载到JVM中。动态代理的源码在程序运行期间生成,因此不存在代理类的字节码文件。
优点:
无需手动编写代理类,减少了代码冗余和提高了可维护性。
适用于目标对象接口不确定或需要频繁变更的场景。
可以实现更复杂的代理逻辑,如AOP(面向切面编程)等。
缺点:
由于动态代理是基于反射实现的,因此相对于静态代理来说,性能上可能稍逊一筹。
动态代理只能代理实现了接口的类,不能代理实现了抽象类或继承了具体类的对象。
4、InvocationHandler
InvocationHandler 是一个接口,它是动态代理模式的核心部分。当使用 Proxy 类和 InvocationHandler 接口来创建动态代理时,你实际上是在创建一个实现了指定接口的代理类实例,该实例在方法调用时会转发到 InvocationHandler 的 invoke 方法。
5、静态代理实现思路
(1)、定义规范接口
(2)、编写实现类1,实现上诉规范接口
(3)、编写代理类,实现上诉规范接口,同时将实现类注入为代理类的成员属性,编写构造方法或其他方法实例化这个成员属性。
(4)、编写代理类的实现方法,通过引用成员实现类的相同方法实现,可以在前后进行相关的业务扩展。
(5)、生成代理对象,执行业务方法
6、动态代理实现思路
(1)、定义规范接口
(2)、编写实现类1,实现上诉规范接口
(3)、编写InvocationHandler接口的实现方法,扩展业务需要,如之前过滤数据,之后记录日志等。
(4)、通过Proxy对象和实现的InvocationHandler类创建目标对象的代理对象
(5)、调用代理对象,执行业务方法
二、代码示例
1、静态代码示例
// 1、定义规范接口
public interface CarApply {
public void useCar(String user,String date);
}
// 2、创建实现类
public class EmployeeCarApply implements CarApply{
@Override
public void useCar(String user, String date) {
System.out.println(user+"在"+date+"申请用车");
}
}
// 3、创建代理类
public class ProxyCarApply implements CarApply {
private CarApply carApply;
public ProxyCarApply(CarApply carApply) {
this.carApply = carApply;
}
@Override
public void useCar(String user, String date) {
if (user.contains("经理")) {
System.out.println(date + "给" + user + "安排一名司机");
}
carApply.useCar(user, date);
if (user.contains("经理")) {
System.out.println("2个小时后给" + user + "订餐");
}
}
}
// 测试
public static void main(String[] args) {
EmployeeCarApply employeeCarApply1 = new EmployeeCarApply();
employeeCarApply1.useCar("李四经理","2024年3月15日");
System.out.println();
ProxyCarApply proxyCarApply = new ProxyCarApply(new EmployeeCarApply());
proxyCarApply.useCar("李四经理","2024年3月16日");
}
运行结果:
直接用目标类仅做了基础功能,通过代理类在执行目标类的功能前后做了一些前置和后置的业务扩展。
2、动态代码示例
// 1、定义规范接口
public interface CarApply {
void useCar(String user, String date);
}
// 2、创建实现类
public class EmployeeCarApply implements CarApply {
@Override
public void useCar(String user, String date) {
System.out.println(user+"在"+date+"申请用车");
}
}
// 3、创建InvocationHandler的实现类,标识动态代理,进行接口功能转发到本类的invoke方法上
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CarApplyInvocationHandler implements InvocationHandler {
private Object target;
public CarApplyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用之前的逻辑");
Object result = method.invoke(target, args); // 执行真实的方法
System.out.println("调用之后的逻辑");
return result;
}
}
// 4、创建Proxy的代理工厂,通过此工厂动态生成代理对象
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyFactory {
public static <T> T getProxyInstance(Object target){
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler handler = new CarApplyInvocationHandler(target);
return (T) Proxy.newProxyInstance(classLoader,interfaces,handler);
}
}
// 5、测试
public static void main(String[] args) {
CarApply proxyObj = ProxyFactory.getProxyInstance(new EmployeeCarApply());
proxyObj.useCar("张三","2023年3月17日");
}
运行结果:
由下结果可以看出,当执行代理对象的业务方法时,之际执行了InvocationHandler实现类的invoke方法。
3、总结
代理的作用就是在不改变原代码的同时,对原代码的功能做业务扩展。分为静态和动态两种代理方式,静态代理需要我们在开发过程中就先创建好代理的类,直接使用代理类完成原始类的功能,思路和设计也都比较简单。动态代理则是在运行时段,基于反射生成代理类的源码,主要用到了两个对象(Proxy和InvocationHandler接口),相对静态代理而言,代码编写上难度大一点,但是动态代理方式无需大量编写代理对象,如果原始类放生变动,静态代理则是比较奔溃的局面,代理类往往也需要改动,动态代理则相对改动较少。各有优缺点,建议可以在代理类比较少的情况下使用静态代理,其他情况使用动态代理,不确定情况也使用动态代理,多写两次就会写了。
学海无涯苦作舟!!!