代理模式(Proxy Pattern)
代理模式(Proxy Pattern)
代理模式是一种结构型设计模式,它为其他对象提供一个代理,以控制对这个对象的访问。代理模式通过引入一个代理对象,可以在不改变原始对象的情况下,增加额外的功能或控制访问。
模式结构
- 服务接口(Subject)
定义服务接口,代理和真实服务对象都必须实现该接口。 - 真实服务类(Real Subject)
提供具体的业务逻辑,是代理对象所代表的真实对象。 - 代理类(Proxy)
实现服务接口,并持有一个真实服务对象的引用。代理类可以在调用真实服务对象之前或之后执行额外的操作。 - 客户端(Client)
通过服务接口与代理对象交互,代理对象将请求转发给真实服务对象。
代码示例
1. 服务接口(Subject)
// 服务接口:定义服务接口
interface CommandExecutor {
void run();
}
2. 真实服务类(Real Subject)
// 真实服务类:提供具体的业务逻辑
class RealCommandExecutor implements CommandExecutor {
@Override
public void run() {
System.out.println("命令执行中 ...");
}
}
3. 代理类(Proxy)
// 静态代理类:实现服务接口,并持有一个真实服务对象的引用
class CommandExecutorProxy implements CommandExecutor {
private final CommandExecutor commandExecutor;
public CommandExecutorProxy(CommandExecutor commandExecutor) {
this.commandExecutor = commandExecutor;
}
@Override
public void run() {
System.out.println("代理启动 ...");
commandExecutor.run();
System.out.println("代理结束 ...");
}
}
4. 动态代理工厂(Dynamic Proxy Factory)
// 动态代理工厂:创建动态代理对象
class DynamicProxyFactory {
private final Object target;
public DynamicProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理启动 ...");
Object res = method.invoke(target, args);
System.out.println("动态代理结束 ...");
return res;
}
}
);
}
}
5. CGLib 代理工厂(CGLib Proxy Factory)
// CGLib 代理工厂:创建 CGLib 代理对象
class CGLibProxyFactory implements MethodInterceptor {
private final Object target;
public CGLibProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
Enhancer en = new Enhancer();
en.setSuperclass(target.getClass());
en.setCallback(this);
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB 代理启动 ...");
Object invoke = method.invoke(target, objects);
System.out.println("CGLIB 代理结束 ...");
return invoke;
}
}
6.调用示例
public class ProxyPattern {
public static void main(String[] args) {
System.out.println("---------- 静态代理 ----------");
RealCommandExecutor commandExecutor = new RealCommandExecutor();
CommandExecutorProxy proxy = new CommandExecutorProxy(commandExecutor);
proxy.run();
System.out.println("---------- 动态代理 ----------");
CommandExecutor dynamicProxy = (CommandExecutor) new DynamicProxyFactory(commandExecutor).getProxyInstance();
dynamicProxy.run();
System.out.println("---------- CGLIB 代理 ----------");
CommandExecutor cglibProxy = (CommandExecutor) new CGLibProxyFactory(commandExecutor).getProxyInstance();
cglibProxy.run();
}
}
输出结果
---------- 静态代理 ----------
代理启动 ...
命令执行中 ...
代理结束 ...
---------- 动态代理 ----------
动态代理启动 ...
命令执行中 ...
动态代理结束 ...
---------- CGLIB 代理 ----------
CGLIB 代理启动 ...
命令执行中 ...
CGLIB 代理结束 ...
应用场景
- 延迟初始化(虚拟代理)
当需要延迟初始化一个重量级对象时,可以使用代理模式。代理可以在真正需要时才创建和初始化对象。 - 访问控制(安全代理)
当需要控制对某个对象的访问权限时,可以使用代理模式。代理可以在访问对象之前进行权限检查。 - 远程代理
当对象位于远程服务器上时,可以使用代理模式。代理负责处理网络通信,客户端无需关心远程调用的细节。 - 日志记录(日志代理)
当需要记录对对象的访问日志时,可以使用代理模式。代理可以在调用对象之前或之后记录日志。 - 缓存(缓存代理)
当需要缓存对象的请求结果时,可以使用代理模式。代理可以在调用对象之前检查缓存,避免重复计算。
优缺点
优点
- 代理模式能将代理对象与真实被调用的目标对象分离。
- 可以扩展目标对象的功能。
- 即使服务对象还未准备好或不存在, 代理也可以正常工作。
- 你可以在不对服务或客户端做出修改的情况下创建新代理,满足开闭原则。
- 可以很好的保护目标对象。
缺点
- 复杂性增加
代理模式引入了额外的类和接口,增加了代码的复杂性。 - 性能开销
代理模式可能会引入额外的性能开销,特别是在频繁调用时。
总结
代理模式通过引入一个代理对象,可以在不改变原始对象的情况下,增加额外的功能或控制访问。它适用于需要延迟初始化、访问控制、远程调用、日志记录和缓存等场景。尽管它可能会增加代码的复杂性,但其优点在于提高了代码的灵活性和可扩展性。代理模式在需要控制对象访问或增加额外功能的场景中非常有用。