23天设计模式之代理模式

23天设计模式之代理模式

文章简介

《23天设计模式之代理模式》是在最近学习反射与注解时,在反射中有关Proxy类的知识,也就顺带复习一下代理模式,总结博客。

代理模式

  • 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

  • 组成:

    • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

    • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

    • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

  • 举个例子:

    • 比如 “租房子”、“租客”、“中介”就分别代表 “抽象角色”、“真实角色”、“代理角色”。
    • “租客”与“中介”都要实现“租房子”接口。
    • 将“租客”注入到“中介”中,由“中介”调用“租客”的实现方法。
  • 代理分为静态代理和动态代理。

    • 静态代理:手动生产代理类。
    • 动态代理:自动生成代理类。

静态代理

直接上代码:

  • 抽象角色
// 租房子 - 抽象角色,用接口或抽象类表示,里面放真实角色要实现的业务方法
public interface Rent {
    void rent();
}
  • 真实角色
// 租客 - 真实角色
public class Tenant implements Rent {

    private String name;

    public Tenant(String name) {
        this.name = name;
    }

    @Override
    public void rent() {
        System.out.println("租客:" + name + " 租到房子了");
    }
}
  • 代理角色
// 静态代理类 - 代理角色
public class StaticProxy implements Rent{

    // 将被代理角色注入进来
    private Tenant tenant;

    public void setTenant(Tenant tenant) {
        this.tenant = tenant;
    }

    public void beforeRent() {
        System.out.println("租房前,中介带租客看房");
    }

    public void afterRent() {
        System.out.println("租房后,中介收取中介费和房租");
    }

    @Override
    public void rent() {
        beforeRent();
        tenant.rent(); // 执行真实角色的代理方法,租房前后可以插入其它业务代码。
        afterRent();
    }
}
  • 测试
public class Test {

    public static void main(String[] args) {
        Tenant tenant = new Tenant("孤影");
        StaticProxy proxy = new StaticProxy();
        proxy.setTenant(tenant);
        proxy.rent(); // 可以发现,在这里已经是代理类在执行代理方法, 而不是Tenant类在执行
    }
}

// 输出
租房前,中介带租客看房
租客:孤影 租到房子了
租房后,中介收取中介费和房租

动态代理

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理。
    • 基于接口:jdk动态代理。
    • 基于类:cglib。
    • java字节码实现:javassist。
  • 这里我们主要了解jdk动态代理。
  • 面试时问道我们动态代理是怎么实现的?我们通常只是回答通过反射实现的,但是具体的却说不出东西来,因此一定了解Proxy类和InvocationHandler接口。

接下来上代码:

  • 抽象角色和真实角色与上文相同。
  • 定义一个InvocationHandler,以此来调用代理的方法。
// 动态代理类处理程序 - 通过此类获取代理角色,得到代理类后,使用代理类执行代理方法
public class DynamicProxyHandler implements InvocationHandler {

    // 注入被代理对象
    private Object target;

    private String before;

    private String after;

    public void setTarget(Object target) {
        this.target = target;
    }

    public DynamicProxyHandler(String before, String after) {
        this.before = before;
        this.after = after;
    }

    // 生成代理角色
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    public void beforeInvoke(String s) {
        System.out.println(s);
    }

    public void afterInvoke(String s) {
        System.out.println(s);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeInvoke(before);
        Object result = method.invoke(target, args);// 通过反射执行代理方法,前后可插入其它业务代码
        afterInvoke(after);
        return result;
    }
}
  • 测试
public class Test {

    public static void main(String[] args) {
        Tenant tenant = new Tenant("孤影");
        DynamicProxyHandler handler = new DynamicProxyHandler("租房前,中介带租客看房", "租房后,中介收取中介费和房租");
        handler.setTarget(tenant);
        Object proxy = handler.getProxy();
        if (proxy instanceof Rent) { // 可以看到,生成的代理类仍然是抽象角色的一个实例
            ((Rent) proxy).rent();
        }
    }
}

// 输出
租房前,中介带租客看房
租客:孤影 租到房子了
租房后,中介收取中介费和房租
  • 疑惑:

    • 有同学可能会疑惑生成代理角色时使用的this.getClass().getClassLoader(),为什么不是target.getClass().getClassLoader()

    • 其实二者获取的类加载器都是同一个AppClassLoader。

    • 通过打印测试确实是同一个类加载器:

      // 生成代理角色
      public Object getProxy() {
          System.out.println(this.getClass().getClassLoader());
          System.out.println(target.getClass().getClassLoader());
          return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
      }
      
      // 输出
      sun.misc.Launcher$AppClassLoader@18b4aac2
      sun.misc.Launcher$AppClassLoader@18b4aac2
      
    • 关于类加载器要了解更多请阅读我的另一篇文章 – 简单谈谈对GC垃圾回收的通俗理解,关于类加载器和双亲委派机制讲的也比较详细。

动态代理的好处

  • 可以使真实角色的操作更纯粹!不用去关注一些公共的业务。
  • 公共业务就交给代理角色,实现了业务的分工。
  • 公共业务发生扩展时,方便集中的管理。
  • 一个动态代理类代理的是一个接口,一般就是对应一类业务。
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可。

以上

感谢您花时间阅读我的博客,以上就是我对代理模式的一些理解,若有不对之处,还望指正,期待与您交流。

本篇博文系原创,仅用于个人学习,转载请注明出处。

版权声明:本文为guying-blog原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/guying-blog/p/14907174.html