LOADING

加载过慢请开启缓存 浏览器默认开启

设计模式-Proxy代理模式

2023/7/24 java DesignPattern
  1. Proxy代理模式
    1. what
    2. why
    3. structure
    4. 具体例子
    5. how
      1. 静态代理
      2. 动态代理
      3. CGLIB动态代理
      4. 多级动态代理
    6. when
    7. P&Cs
    8. summary

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的实现原理不言而喻。