设计模式——动态代理与静态代理

一.代理模式的定义:

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。生活中比较常见的代理模式的应用比如:火车票代购、代办保险、UU 跑腿、武侠片中的替身,可以理解为对原工作的一种增强实现

如下1-不使用代理

请添加图片描述

如下2-使用代理模式
请添加图片描述
那么,为什么会存在代理模式呢?我们知道,存在即合理,很多情况下,客户类不想或不能直接引用委托对象,这时候使用代理类充当中介作用,这种情况下代理类和委托类实现相同的接口;另外,有时候我们会想增强委托类,这个时候使用代理类来完成也是再合适不过了,也是符合开闭原则(对拓展开放,对修改关闭)的。

二.角色组成:

代理模式也叫做委托模式,代理类一般包含被委托类的引用,下面我们来说下上面三个角色的定义:

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

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

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

三.使用的具体实例

1.静态代理

例子:我们以房东出租房子为例子 定义一个租房接口Rent,然后定义一个具体的实现类房东Host
请添加图片描述
租房(Rent)接口的代码

//租房接口
public interface Rent {

    public void rent();

}

然后是房东(Host)的具体实现类

//房东
public class Host implements Rent{
    public void rent() {
        System.out.println("房东要出租房子!!");
    }
}

最后使我们的测试类:

public class Client {

    public static void main(String[] args) {
        Rent rent = new Host();
        //房东开始租房
        rent.rent();
    }
}

测试结果如下:

房东要出租房子!!

请添加图片描述

我们从房东租房子可以看到其实代码还是比较简单的 贴近于生活,但在生活中往往不是这样的,房东一般不止一套房子,如果是自己宣传租房子比较麻烦,自己有比较忙没时间管这些事情,这时候中介就诞生了,房东通常将房子交给中介,有中介负责租房子,自己只负责收钱就可以了

这里我们增加Proxy代表代理类,租客首先找中介,由中介来进行租房相关业务,代码如下

/**
* @Desc 中介(代理类)
* @Author wfs
*/
public class Proxy {

    //房东
    private Host host;


    public Proxy(Host host) {
        this.host = host;
    }

    public Proxy(){

    }
    public void rent(){
        host.rent();
        seeHouse();
        hetong();
        fare();
    }

    //收中介带你看房
    public void seeHouse(){
        System.out.println("中介带你看房!!");
    }


    //合同
    public void hetong(){
        System.out.println("签租聘合同!!");
    }

    //收中介费
    public void fare(){
        System.out.println("收中介费!!");
    }
}

测试及输出如下:

房东要出租房子!!
中介带你看房!!
签租聘合同!!
收中介费!!

可以看出,租客在租房子的同时,还增强了其他功能,在不改变原有代码下,实现功能增强

符合七大原则对扩展开放,对修改关闭

2.动态代理

上面讲了一下静态代理的基础实例,接下来我们学习下什么是动态代理,在上面静态代理中我们使用手动创建的代理类来实现业务代理,倘若我们现在要服务多种类型的对象,那么是不是要为每个对象都要建立一份代理,这显然是不切实际的。另外静态代理中的接口一旦发生变化,那么意味着代理类也要进行重写,这在大型项目中是不允许的,针对以上两种情况,所以就产生了动态代理。

2.1.jdk动态代理

jdk 的动态代理是通过反射技术来创建类的加载器并且创建类的实例,根据类执行方法并在方法执行前后进行前置或者后置通知等处理。使用到的就是 Proxy 类的静态方法 newProxyInstance,该方法会返回一个代理类对象:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

下面我们使用 jdk 的动态代理来实现下上面 Proxy 代理类的功能,这里就直接贴代码来看吧:

// 自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
 
    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成代理对象 生成动态代理实例
    /**
     * 生成代理类Proxy.newProxyInvocation(参数1.参数2,参数3)
     * 参数1:表名你要用那个类加载器去加载生成的代理类。(这个是JVM类加载的知识,可以去了解一下,挺简单)。
     * 参数2:说明你要生成的代理类的接口。
 	 * 参数3:实现了InvocationHandle的类,这个类只有一个方法需要你要实现它。
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    //处理代理实例并返回实例
     /**
     * invoke(Object proxy, Method method, Object【】 args) {
	 * 这个方法第一个参数,是生成的代理类,目前没发现用处,不管它。
 	 * 第二个参数,是执行的方法(利用反射的原理,可以去看反射,也很简单。)
	 * 第三个参数,是执行某方法需要的参数。
 	 * 第二个第三个参数解释执行的方法意思是:代理类不是要代理某个对象么,然后增强里面的方法么,指得就是这个方法,代理类会为几乎所有方法都增强,除非你在这里做判断。
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //动态代理的本质,就是使用反射机制实现 调用处理程序并返回一个结果
        /**
          * 返回值,是执行这个方法所返回的值。
 		 * 然后你要去执行方法,就是用第二参数的invoke(obj,args);第一个参数是你要增强的对象。第二个是参数。object是你返回的类型
 		 * Object object= method.invoke(obj,args);
          */
        Object result = method.invoke(rent, args);
         System.out.println("中介带你去看房");
         System.out.println("签订租房合同");
        return result;
    }

}

最后使我们的测试类:

public class Client {
    public static void main(String[] args) {

        //真实角色 房东
        Host host = new Host();

        //代理角色 现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象 
        pih.setRent(host);

        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();

    }
}

测试结果如下:

带你去看房

可以看到,使用 jdk 动态代理相比静态代理优势更加明显,免去了代理类的编写,由于使用了反射技术,往往 jdk 动态代理效率没有静态代理

2.2.cglib动态代理

jdk 的动态代理使用的静态方法第二个参数就是目标对象实现的接口类型,所以需要被代理的目标对象实现一个或多个接口,倘若目标对象没有实现的接口怎么办?这个时候就可以考虑 cglib 动态代理。

如下房东类是没有实现接口的 就是一个普通的类,我们要对该类实现类似前置、后置操作就可以使用 cglib 动态代理

房东类


public class Host {

    public void rent(){
        System.out.println("房东要出租房子!");
    }

}

cglib代理类

/**
 * 自定义ProxyMethodInterceptor
 */
public class ProxyMethodInterceptor implements MethodInterceptor {

    //被代理的类
    private Host host;

    public void setHost(Host host) {
        this.host = host;
    }

    /**
     * sub:cglib生成的代理对象
     * method:被代理对象方法
     * objects:方法入参
     * methodProxy: 代理方法
     */
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("带你去看房");
        System.out.println("签订租房合同");
        return object;
    }
}

测试类

public class Client {
    public static void main(String[] args) {

        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        //设置代理对象的父类
        enhancer.setSuperclass(Host.class);
        //设置enhancer的回调对象
        enhancer.setCallback(new ProxyMethodInterceptor());
        //创建代理对象
        Host host = (Host)enhancer.create();
        //通过代理对象条用目标方法
        host.rent();
    }

}

测试结果如下:

房东要出租房子!
带你去看房
签订租房合同

总结:

我们介绍了代理模式的定义、应用等等,又列举了静态代理和动态代理以及相应的实例,想必大家已经知道代理模型的具体含义了,下面我们总结下本节的内容:

请添加图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只可爱的委屈翁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值