Proxy代理模式
what
原型模式代理是一种结构型设计模式,让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问,并允许在将请求提交给对象前后进行一些处理。
why
代理模式提供了一种灵活和模块化的方法来控制、优化和增强与对象的交互。它允许在不修改真实对象的情况下引入额外的行为,促进关注点分离,并为客户端和正在访问的对象之间提供一层间接性。
structure


具体例子
来自GURU
信用卡和现金在支付过程中的用处相同。
how
代理模式建议新建一个与原服务对象接口相同的代理类, 然后更新应用以将代理对象传递给所有原始对象客户端。
代理类接收到客户端请求后会创建实际的服务对象, 并将所有工作委派给它。
这有什么好处? 如果需要在类的主要业务逻辑前后执行一些工作,无需修改类就能完成。
由于代理实现的接口与原类相同, 因此可将其传递给任何一个使用实际服务对象的客户端。
静态代理
具体例子: 上课传纸条给妹子 🤓 中间人(代理)闺蜜
定义一个共同的接口(即大家要做的事请:放学一起打游戏)
public interface Subject {
// 共同的接口
void doSomething();
}
构建一个真实对象,即例子中的 妹子同学C 能做到的事
public class RealSubject implements Subject {
// 真实对象
@Override
public void doSomething() {
System.out.println("放学去打游戏");
}
}
构建代理对象 妹子闺蜜B
public class ProxySubject implements Subject {
private RealSubject realSubject;
//方式一
public ProxySubject(RealSubject realSubject) {this.realSubject = realSubject;}
//方式二
public ProxySubject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
this.realSubject = (RealSubject) this.getClass().getClassLoader()
.loadClass("com.proxyPattern.RealSubject").newInstance();
}
@Override
public void doSomething() { realSubject.doSomething(); }
public static void main(String[] args) {
try { // 第一种方式
new ProxySubject().doSomething();
// 打印结果: 放学去打游戏
} catch (Exception e) { // 异常情况,代理失败,
// 传纸条的被老师抓了。或者妹子C不在 等异常情况
}
// 第二种方式
new ProxySubject(new RealSubject()).doSomething();
// 打印结果: 放学去打游戏
}
}
在Main方法里面,有两种方式来调用真实对象:
- 第一种:采用类加载器形式,去加载实列对象,这样我们就不用关心到底什么时候需要真实的实列化对象
- 第二种:通过传值的形式,把实列化对象传过来。(理解为装饰器模式了)
这里要区别一下,代理模式是提供完全相同的接口,而装饰器模式是为了增强接口。
动态代理
静态代理需要为每一个对象都创建一个代理类,增加了维护开发成本。
而动态代理避免了静态代理,在运行时通过反射机制创建代理类。
使用动态代理,不再需要具体的实现类RealSubject。
public interface Subject {
void doSomething();
}
创建一个新的动态代理类,实现InvocationHandler接口。这个类将作为动态代理的处理程序
public class DynamicProxyHandler implements InvocationHandler {
private Subject realSubject;
public DynamicProxyHandler(Subject realSubject) { this.realSubject = realSubject;}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object/int/... result;
// 在方法调用前后可以添加你的逻辑
// 这里简单打印一下方法调用
System.out.println("Before method: " + method.getName());//增强/次要业务
result = method.invoke(realSubject, args);//主要业务 不关注返回值可以直接调用即可
System.out.println("After method: " + method.getName());
// if(){ result = ;} else result = ;
return result;
}
}
在invoke方法中,可以添加在方法调用前后执行的逻辑。
method.invoke(obj, args)方法用于执行被代理对象obj的method方法,同时传递给该方法的参数是args。
最后,修改ProxySubject类以使用动态代理:
public class ProxySubject {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
DynamicProxyHandler handler = new DynamicProxyHandler(realSubject);
Subject proxySubject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler
);
proxySubject.doSomething();
}
}
使用动态代理有三个要点,
- 必须实现 InvocationHandler 接口,表明该类是一个动态代理执行类。
- InvocationHandler 接口内有一实现方法如下:
public Object invoke(Object proxy, Method method, Object[] args) 。使用时需要重写这个方法 - 获取代理类,需要使用 Proxy.newProxyInstance(Clas loader, Class[] interfaces, InvocationHandler h) 这个方法
去获取Proxy对象(Proxy 类类型的实例)。
CGLIB动态代理
CGLIB(Code Generation Library)是Code生成类库(代码生成包)。
Hibernate用它实现PO(Persistent Object 持久化对象)字节码的动态生成,Spring AOP用它提供方法的interception(拦截)。
貌似暂时用不到 先不看了
多级动态代理
简单理解,螳螂捕蝉黄雀在后
动态代理本质可以理解为将“次要业务”与“主要业务”解耦合,让开发者能更加专注于主要业务,提升开发效率,以及维护成本。
那么次要业务也可以分为次要中的“次要业务”和“主要业务”。
同上
when
Spring AOP
JDBC代理
延迟初始化(虚拟代理)
访问控制(保护代理)
本地执行远程服务(远程代理)
记录日志请求(日志记录代理)
缓存请求结果(缓存代理)
只能引用:客户端不调用的时候直接销毁服务对象
P&Cs
P:
- 可以在客户端毫无察觉的情况下控制服务对象。
- 如果客户端对服务对象的生命周期没有特殊要求, 你可以对生命周期进行管理。
- 即使服务对象还未准备好或不存在, 代理也可以正常工作。
- 开闭原则。 可以在不对服务/客户端做出修改的情况下创建新代理。
C:
- 代码可能会变复杂, 需要新建许多类。
- 服务响应可能会延迟。
summary
代理模式,实际作用可以用来解耦业务和非功能性需求,比如增加一个代理类,专门用来处理监控、统计、鉴权、限流、事务、幂等、日志。而被代理类可以专心处理业务逻辑。
- 代理模式在业务代码上比较少见,特别是动态代理。主要是AOP的实现原理不言而喻。
