• Java设计模式之——代理设计模式


    1.什么是代理设计模式

    所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。

    这里举一个栗子:就拿我们平时租房子来举例子,好比租客和房主之间的关系,我们租房子往往不会挨个去找房东,而是通过中间的代理者,也就是中介完成,租客来间接的和房主接触,这个时候租客(Tenant)相当于用户中介(AgencyProxy)相当于代理者房主(Homeowner)相当于被代理者。

    在代理模式中还分为两种模式:静态代理和动态代理,下面我们通过代码来演示以下两种代理方式。

    2、静态代理

    首先我们先创建一个房主的接口(Houseowner),接口中只有一个租房子的方法。

    1 /**
    2  * @author wzy
    3  * @version 1.0
    4  * @date 2019/5/9 15:31
    5  */
    6 public interface Homeowner {
    7     public void letHouse();
    8 }

    之后我们定义一个类去实现这个接口

     1 /**
     2  * @author wzy
     3  * @version 1.0
     4  * @date 2019/5/9 15:22
     5  */
     6 public class HomeownerImpl implements Homeowner {
     7 
     8     public void letHouse() {
     9         System.out.println("房东:出租了一套房子");
    10     }
    11 }

    房主出租房子一般都会交给中介代理,创建一个AgencyProxy类,我们可以看到下面的类,这个类实现了被Homeowner接口,将房主的实现类作为类的成员变量,并且在自己的letHouse方法中调用目标类的方法,并且可以在调用目标类的前后做一些操作。

     1 public class AgencyProxy implements Homeowner{
     2     //被代理的对象
     3     private HomeownerImpl target;
     4 
     5     public AgencyProxy(HomeownerImpl target) {
     6         this.target = target;
     7     }
     8 
     9     public void letHouse() {
    10         System.out.println("中介带租客看房子");
    11         target.letHouse();
    12         System.out.println("成交后中介收取服务费");
    13     }
    14 
    15 }

    接下来,创建一个租客类Tenant进行测试

    1 public class Tenant {
    2     public static void main(String[] args) {
    3         //租房子
    4         HomeownerImpl homeowners = new HomeownerImpl();
    5         AgencyProxy agencyProxy = new AgencyProxy(homeowners);
    6         agencyProxy.letHouse();
    7 
    8     }
    9 }

    输出结果:

    我们可以看到输出的结果,在调用被代理类的方法前后,代理类都可以做一些操作,这样就可以达到解耦的目的,也可以保护被代理的对象的目的,现在我们思考一下,如果被代理类的方法很多,那么我们每次都要在代理类中将所有方法重写一遍吗?并且如果被代理类中的方法名称发生变化,我们就需要去修改代理类的代码,这显然是不科学的,然而,动态代理就解决了这一问题。

    3.动态代理

    动态代理分为两种:JDK动态代理和cglib动态代理,动态代理的底层原理是,在程序运行时,通过反射机制动态生成代理类,那么如何实现动态代理呢?我们通过创建一个实现InvocationHandler的类,实现其中的invoke方法,在invoke方法对目标类中的方法进行调用。之后通过Proxy.newProxyInstance()创建一个动态代理的对象。

    保持其他代码不动,修改AgencyProxy类的代码:

     1 public class AgencyProxy implements InvocationHandler{
     2     //被代理的对象,目标类
     3     private Homeowner target;
     4     //通过构造函数传入被代理目标类
     5     public AgencyProxy(Homeowner target) {
     6         this.target = target;
     7     }
     8 
     9     public Homeowner getProxy() {
    10         //目标类的类加载
    11         ClassLoader loader = target.getClass().getClassLoader();
    12         //返回代理类的接口列表
    13         Class [] classes = target.getClass().getInterfaces();
    14         //最后一个参数返回的实现了InvocationHandler的代理类
    15         Homeowner homeowner = (Homeowner) Proxy.newProxyInstance(loader, classes, this);
    16         //返回代理类对象
    17         return  homeowner;
    18     }
    19 
    20     @Override
    21     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    22         System.out.println("中介在租房前操作。。。。");
    23         //动态执行代理目标类中的方法
    24         Object result = method.invoke(target);
    25         System.out.println("中介在租房后的操作。。。");
    26         return result;
    27     }
    28 }

    修改租客类Tenant测试动态代理:

     1 public class Tenant {
     2     public static void main(String[] args) {
     3         //创建被代理类:房主类
     4         Homeowner homeowners = new HomeownerImpl();
     5         //创建代理类,并传入被代理对象
     6         AgencyProxy agencyProxy = new AgencyProxy(homeowners);
     7         //返回生成的对象
     8         Homeowner homeownerProxy = agencyProxy.getProxy();
     9         //调用方法
    10         homeownerProxy.letHouse();
    11     }
    12 }

    输出结果:我们可以看到通过动态代理我们实现了同样的效果。

    4.总结

            动态代理是一种十分常用的设计模式,在各种开源框架中都得到了非常广泛的应用,例如Spring的AOP底层就是使用的动态代理,MyBatis底层去代理Mapper使用的也是动态代理,还有就是在日志输出上也会用到这种设计模式,它的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法都可以)。

  • 相关阅读:
    分析SIX锁和锁分区导致的死锁
    导数中的最小化日志记录:测试和分析
    导数中的最小化日志记录:背景和理论
    Redis学习笔记(十一) 服务器
    Redis学习笔记(十) 客户端
    Redis学习笔记(九) AOF持久化
    Redis学习笔记(八) RDB持久化
    Redis学习笔记(七) 数据库
    Redis学习笔记(六) 对象
    Redis学习笔记(五) 压缩列表
  • 原文地址:https://www.cnblogs.com/fengyun2019/p/10839200.html
Copyright © 2020-2023  润新知