一、核心思想
调用方 --> 代理 ---> 目标对象
代理是调用真实目标对象的一层中间层,如果需要进行
①、屏蔽复杂对象的创建过程,让上层调用友好感知,并起到隐藏保护的目的。
②、一定的方法扩展增强
③、或者在对目标对象调用时,做一些权限前置校验。
二、静态代理,JDK和CGLIB动态代理
代理模式结构:
其中,Client是客户端类,它需要使用Subject接口提供的方法。Proxy是代理类,它持有一个RealSubject的引用,并且实现了Subject接口。RealSubject是真实类,它也实现了Subject接口,并且定义了具体的业务逻辑。
代理模式有多种类型,例如静态代理、动态代理等,代理模式也有自己的优缺点,使用时需要根据具体的场景和需求来选择合适的类型和方式。
静态代理实现
下面我们用Java代码来实现一个静态代理的例子:
// 抽象主题接口
public interface Subject {
// 定义一个抽象方法
void request();
}
// 真实主题类
public class RealSubject implements Subject {
// 实现抽象方法
@Override
public void request() {
// 真实的业务逻辑
System.out.println("RealSubject is doing something...");
}
}
// 代理主题类
public class Proxy implements Subject {
// 持有一个真实主题的引用
private RealSubject realSubject;
// 构造方法,传入一个真实主题对象
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
// 实现抽象方法
@Override
public void request() {
// 在调用真实主题之前,可以执行一些额外操作
System.out.println("Proxy is doing something before...");
// 调用真实主题的方法
realSubject.request();
// 在调用真实主题之后,可以执行一些额外操作
System.out.println("Proxy is doing something after...");
}
}
// 客户端类
public class Client {
public static void main(String[] args) {
// 创建一个真实主题对象
RealSubject realSubject = new RealSubject();
// 创建一个代理对象,并传入真实主题对象
Proxy proxy = new Proxy(realSubject);
// 使用代理对象来调用抽象方法
proxy.request();
}
}
运行结果如下:
Proxy is doing something before...
RealSubject is doing something...
Proxy is doing something after...
从运行结果可以看出,代理对象在调用真实对象的方法之前和之后,都执行了一些额外的操作,从而对真实对象进行了增强或控制。
动态代理实现
动态代理是一种特殊的代理模式,它可以在运行时动态地创建代理对象,而不需要事先定义代理类。动态代理可以更灵活地适应不同的场景和需求,但是也更复杂和难以理解。
这个例子是使用JDK动态代理来实现一个日志代理,它可以在调用目标对象的方法之前和之后,记录相关的日志信息。代码如下:
// 抽象主题接口
public interface Subject {
// 定义一个抽象方法
void request();
}
// 真实主题类
public class RealSubject implements Subject {
// 实现抽象方法
@Override
public void request() {
// 真实的业务逻辑
System.out.println("RealSubject is doing something...");
}
}
// 日志处理器类,实现了InvocationHandler接口,用于定义代理逻辑
public class LogHandler implements InvocationHandler {
// 持有一个目标对象的引用
private Object target;
// 构造方法,传入一个目标对象
public LogHandler(Object target) {
this.target = target;
}
// 实现invoke方法,用于调用目标对象的方法,并在之前和之后执行日志操作
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用目标对象之前,记录开始时间
long startTime = System.currentTimeMillis();
System.out.println("开始执行" + method.getName() + "方法...");
// 调用目标对象的方法,并获取返回值
Object result = method.invoke(target, args);
// 在调用目标对象之后,记录结束时间和耗时
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("结束执行" + method.getName() + "方法,耗时" + duration + "毫秒");
// 返回结果
return result;
}
}
// 客户端类
public class Client {
public static void main(String[] args) {
// 创建一个真实主题对象
RealSubject realSubject = new RealSubject();
// 创建一个日志处理器对象,并传入真实主题对象
LogHandler logHandler = new LogHandler(realSubject);
// 使用Proxy类的静态方法newProxyInstance来动态地创建一个代理对象,传入真实主题对象的类加载器、接口和处理器
Subject proxy = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), logHandler);
// 使用代理对象来调用抽象方法
proxy.request();
}
}
运行结果如下:
开始执行request方法...
RealSubject is doing something...
结束执行request方法,耗时1毫秒
从运行结果可以看出,代理对象在调用真实对象的方法之前和之后,都执行了一些日志操作,从而对真实对象进行了增强。
CGLIB代理
Java设计模式-代理模式 - 知乎
三、优缺点
优点:
代理的目的更多的是一种对目标对象的隐藏或者是一些行为的增强。
缺点:
非直连创建对象,多一个中间环节,就多了一个份资源的消耗。
三、运用场景
- 网络层面:负载的代理 --》当我们需要访问一个远程对象时,可以使用远程代理,它可以隐藏远程对象的位置和通信细节,让客户端像访问本地对象一样访问远程对象。
- 当我们需要创建一个开销很大的对象时,可以使用虚拟代理,它可以在真正需要的时候才创建真实对象,从而实现延迟加载和节省资源。RabbitMQ缓冲处理信息代理
- 当我们需要给一个对象增加一些额外的功能时,可以使用装饰代理,它可以在不修改原有对象的情况下,给对象添加一些新的行为或属性。
- 当我们需要给一个对象增加一些访问控制或安全保护时,可以使用保护代理,它可以根据不同的用户或角色,对对象的访问进行限制或检查。
- 当我们需要给一个对象增加一些日志记录或性能监控时,可以使用日志代理或性能代理,它可以在调用对象的方法之前或之后,记录相关的信息或数据。