目录
1、核心思想
目的:在不直接操作原始对象的情况下,通过代理对象间接实现功能扩展或访问控制。
基本理念:通过拦截被代理对象的原始业务并在其之前或之后加入一些额外的业务或者控制逻辑,来最终实现在不改变原始类(被代理类)的情况下对其进行加工、管控。常用于延迟加载、权限校验、日志记录等场景。
举例:
1> 房产中介:客户通过中介(代理)了解房源信息,中介负责筛选、验证房东信息
2> VPN代理:用户通过VPN访问网络,VPN隐藏真实IP并可能过滤流量
2、实现方式
2.1 模式结构
三个核心角色:
- Subject(业务接口):定义代理和真实对象的公共接口(如抽象类或接口)
- RealSubject(被代理业务):需要被代理的实际业务类,实际执行业务逻辑的对象
- Proxy(代理):代理类,同样实现了业务接口标准,持有对RealSubject的引用,控制对它的访问并可能添加额外逻辑,对外提供代理后的业务方法

2.2 实现案例
2.2.1 静态代理
-
特点:手动编写代理类,需为每个被代理类创建对应的代理类。
-
适用场景:代理逻辑简单,且被代理对象数量较少时。
// Subject接口
interface Image {
void display();
}
// RealSubject(真实对象)
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(); // 初始化时加载资源(高开销操作)
}
private void loadFromDisk() {
System.out.println("Loading image: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// Proxy(代理对象,延迟加载)
class ProxyImage implements Image {
private String filename;
private RealImage realImage; // 持有真实对象的引用
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // 延迟加载真实对象
}
realImage.display();
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
image.display(); // 第一次调用时加载真实对象
image.display(); // 直接使用已加载的对象
}
}
2.2.2 动态代理
-
特点:在运行时动态生成代理类,无需手动编写代理类代码。
-
实现方式:
-
JDK动态代理:基于接口,使用
java.lang.reflect.Proxy和InvocationHandler。 -
CGLIB动态代理:基于继承,适用于无接口的类。
-
-
适用场景:代理逻辑复杂或需要为多个类统一添加功能(如AOP)。
import java.lang.reflect.*;
// Subject接口
interface UserService {
void saveUser(String name);
}
// RealSubject(真实对象)
class UserServiceImpl implements UserService {
@Override
public void saveUser(String name) {
System.out.println("Saving user: " + name);
}
}
// InvocationHandler实现类(代理逻辑)
/** 实现了JDK反射包中提供的InvocationHandler(动态调用处理器)接口,
** 这个接口定义了动态反射调用的 标准,这意味着LoggingHandler可以代理任意类的任意方法,
** 使万能代理成为可能。
**/
class LoggingHandler implements InvocationHandler {
private Object target; // 被代理的真实对象
public LoggingHandler(Object target) {
this.target = target;//注入被代理对象
}
/** 实现了InvocationHandler的invoke()方法,
** 此处规定要将进行过滤的目标地址字符串放在参数组args的第一个元素位置,
** method.invoke(target, args)利用反射调用被代理的方法,
** 具体被调用的是哪个被代理对象的哪个方法在运行时才能确定下来。
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Method " + method.getName() + " is called.");
Object result = method.invoke(target, args); // 调用真实对象的方法
System.out.println("Method " + method.getName() + " is completed.");
return result;
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
UserService realService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new LoggingHandler(realService)
);
proxy.saveUser("Alice"); // 输出日志并执行方法
}
}
3、优缺点分析
| 优点 | 缺点 |
|---|---|
| 职责清晰(代理与业务逻辑分离) | 增加系统复杂度(需维护代理类) |
| 灵活扩展功能(无需修改目标类) | 动态代理可能降低性能(反射开销) |
| 保护目标对象的安全性 | 静态代理需为每个类编写代理类 |
4、适用场景
-
延迟加载(Lazy Loading)
-
如数据库查询、图片加载,避免不必要的资源消耗。
-
-
访问控制(Access Control)
-
权限校验、防火墙规则等。
-
-
日志记录与监控
-
记录方法调用次数、耗时等。
-
-
缓存代理
-
缓存计算结果,避免重复计算(如频繁访问的API响应)。
-
-
Spring AOP
-
通过动态代理实现事务管理、日志切面等。
-

1626

被折叠的 条评论
为什么被折叠?



