代理模式之静态代理与动态代理

代理模式的简述

对于程序编程而言,代理模式,可以说是一个降低程序与程序之间耦合度的一种方式,它的目的就是为了,在给一个程序添加额外功能的时候,对其原有的代码不做改变,而是对该程序的一个代理程序进行额外功能的增加,这个代理程序,不但能执行好原有程序的功能,还能够执行添加的额外功能。这种模式,尤其是在我们面向对象语言中,是发挥的淋漓尽致,因为我们操作的是一个对象,对象可以抽象为类或者接口,我们就可以通过一个代理类和接口,更加灵活的设计程序,而且对于程序而言,其扩展性和维护性也大大提高,所以代理模式对于我们学习编程的帮助无疑是巨大的。
基本的代理模式一般分为静态代理和动态代理两种:

静态代理

对于静态代理,我们可以很好的理解,因为其就是代理模式的一种最基础,最简单的实现,它体现的就是一个代理关系,举个例子:你要卖掉自己的二手汽车,正常没有代理模式的情况就是,你找到了一个愿意买二手汽车的人,告诉他我要卖掉我的二手汽车,然后双方聊的很嗨,很愉快的达成了交易。skr! —当然这是没有代理人的情况下,但如果你平时工作很忙,你想卖掉车,然而并没有时间去到处找愿意买车的人,这个时候,你就希望找一个代理人来帮你干这件事,代理人可以帮助你找到愿意买车的人,当然前提是要给人家代理费用啦-_-!,然后整个交易的过程就变成了,这个代理人找到一个愿意买车的人,然后,代理人和愿意买车的人谈好,交易完成后,把钱给你,你完成了卖车的理想!大功告成,这就是静态代理的主要思维,当然在我们高大上的Java中来具体实现这个静态代理,就是要先将你和代理人充当的这个卖方先抽象出来,然后再实例化你这个对象和代理人这个对象,当然代理人是替你卖车的,也就是说卖方的核心人物还是你,代理人只是替你找到了一个买家而已。
请看代码:
关于卖方的一个接口

public interface Seller {
public void sellcar();
}

关于“你”的一个实例对象

public class Person implements Seller {

public void sellcar() {
System.out.println("车的主人要卖车");
}
}

关于代理人的一个实例对象:

public class Agent implements Seller{
//添加一个你的对象属性,这是实现代理的一个关键点
private Person person;
//这是确定代理对象的方法
public void setPerson(Person person) {
this.person = person;
}

public void sellcar() {
Introduce();
person.sellcar();
fare();
}
//定义两个代理人特有的方法
private void Introduce(){
System.out.println("我是一个代理人");
}
private void fare(){
System.out.println("我要收代理费");
}
}

新建一个测试类:

public class Test {
public static void main(String[] args) {
Person person = new Person();
Agent agent = new Agent();
agent.setPerson(person);
agent.sellcar();
}
}

测试结果:

在这里插入图片描述

像这样的一个过程就是静态代理,可见对于这个代理人,它除了实现被代理人的功能外,自己还额外的添加进去了自己的方法,这样就实现了开头我们说的再没有改变原有的程序下,添加了额外的功能,这个代理人,就像是另一个自己,不过是经过改造的自己。skr!

总结静态代理的特点

优点

  • 静态代理的一大好处就是,它能够使一个对象专注的完成一项内容,而不用担心以后扩展其他功能的时候,会不会影响到现在的代码设计;
  • 如果需要扩展功能,我们就可以让代理来完成,更加的集中和方便;
  • 缺点
  • 静态代理纵然是对我们的程序设计灵活性和专注性的帮助很大;但是如果我们操作的对象越来越多,也就是说,现在不止你一个人要卖车,有十四亿人民都要卖车,那么我们就需要十四亿个代理人,这样的话,实例化十四亿个代理人对象,工作量是非常大的,所以,我们就希望有一个制造代理人的机器,我们只要给他需要代理服务的人的对象(就是告诉它你要给谁服务),这个机器就能够自动的生成一个代理人,专诚为您服务。它能够自动去创建多个代理人角色,不需要我们一个一个来创建了;这个机器就是接下来即将出场的动态代理;

动态代理

动态代理其实本质就是一句话:抽象一个代理的类!或者说是得到一个代理的接口!拿上面的例子来讲,就是制造一个专门可以代理卖车服务代理人的机器!
实现动态代理现在大概分为两类:
基于接口实现:JDK
基于类实现:cglib

  • JDK动态代理
    下面我主要说这个JDK实现动态代理
    当前来说,我们用的比较多的是JAVAssist来生成动态代理;
    了解之前,需要先掌握两个类

  • InvocationHandler

  • Proxy
    这个InvocationHandler是由代理实例的调用处理程序实现的接口
    *#%$##@#%&^^这句话,语法是有点复杂啊,我来解释一下,这个InvocationHandler是一个接口,一个大体实现什么功能的接口?一个代理实例的调用处理程序需要实现的接口,#¥%¥@¥#……¥@,简单说一下,就是如果我们要制造一个能够自动创造代理人的机器,这个接口就是我们制造机器的工程图纸,代理实例的调用处理程序指的就是这个机器!
    invoke(Object proxy,方法 method,Object[ ] args)处理代理实例上的方法调用并返回结果。
    Proxy提供了创建动态代理类个实例的静态方法,它也是由这些方法创建的所有动态代理类的超类

我们仍旧拿上面你卖车和找代理人卖车的例子来实现

关于卖方的一个抽象接口

public interface Seller {
	public void sellcar();
	}

关于“你”的一个类

public class Person implements Seller {

public void sellcar() {
System.out.println("车的主人要卖车");
}
}

通过通过实现InvocationHandler接口得到的一个代理人的类;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


 public class Machine implements InvocationHandler {
 //和静态代理不同点,这里是先设置一个被代理人的接口属性,这是实现代理的关键点
private Seller seller;

    public void setSeller(Seller seller) {
        this.seller = seller;
    }
    //这个方法是获得一个代理人的方法
    public Object getProxy(){
  这里的一个返回值是一个Proxy.newProxyInstance()方法,这个方法就是通过反射得到一个代理人对象,并将这个对象返回,方法是我们传入一个被代理人的类加载器,再通过反射添加一个被代理人的它实现的接口,再传入这个代理人的类的实例对象
        return Proxy.newProxyInstance(seller.getClass().getClassLoader(),seller.getClass().getInterfaces(),this);

    }
//这个方法是实现接口InvocationHadler的实现方法,
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Introduce();
        //通过反射得到被代理人的方法
        Object invoke = method.invoke(seller, args);
        fare();
        return invoke;
    }
    private void Introduce(){
        System.out.println("我是一个代理人");
    }
    private void  fare(){
        System.out.println("我要收代理费");
    }
}

建立一个测试类:

public class Test {
    public static void main(String[] args) {
        Person person = new Person();
        Machine machine = new Machine();
        machine.setPerson(person);
        Seller proxy = (Seller) machine.getProxy();
        proxy.sellcar();
    }
}

测试结果:
在这里插入图片描述
这样就是我们得到了一动态代理的小demo,为了说明效果,我们可以再新建一个对象来进行测试

关于另一个要卖车人的类:

public class AnotherPerson implements Seller {
    public void sellcar() {
        System.out.println("我是蔡徐坤,除了刚才的人,我蔡徐坤今天也要卖车");
    }
}

新建一个测试类:

public class Test {
    public static void main(String[] args) {
        AnotherPerson anotherPerson = new AnotherPerson();
        Machine machine = new Machine();
        machine.setSeller(anotherPerson);
        Seller proxy = (Seller) machine.getProxy();
        proxy.sellcar();

    }
}

测试结果:
在这里插入图片描述
至此,我们就是通过JDK实现了动态代理,另外的一种动态代理的方法,也就是CGLIB(基于类实现动态代理),它适用于什么类型的呢?如果说,我们现在有一个对象,它并没有接口,只有一个继承类。这样子的话,我们就需要来使用CGLIB实现动态代理。使用CGLIB动态代理的话,我们需要去导入两个jar包分别是CGLIB和ASM .我们同样拿上面的卖车的例子来讲:

  • CGLIB
    建一个卖车的类:

    public class AnotherPerson extends Seller {

      public void sellcar() {
          System.out.println("我蔡徐坤今天也要卖车");
      }
    

    }

建立代理人类:

public class Machine implements MethodInterceptor {
    public Object intercept(Object proxyobject, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    //前置业务处理代码
        System.out.println("我是一个代理人");
     //   invokeSuper方法还是为了获得父类的根方法,同之前的JDK=动态代理相同
        methodProxy.invokeSuper(proxyobject,objects);
    //    后置代理处理代码
        System.out.println("代理完我要收代理费");
        return null;
    }
}

编写测试类:

  import net.sf.cglib.proxy.Enhancer;

public class Test {
    public static void main(String[] args) {
    // 创建Enhancer实例,也就是cglib中的一个class generator
        Enhancer enhancer = new Enhancer();
        // 设置目标类
        enhancer.setSuperclass(AnotherPerson.class);
        // 设置代理人对象
      enhancer.setCallback(new Machine());
      //Seller是person继承的一个抽象父类,这里是多态的体现形式,获得了一个代理人对象,利用多态调用子类重写的方法
        Seller o = (AnotherPerson) enhancer.create();
        o.sellcar();

    }
}

测试结果:
在这里插入图片描述

到这里我们就完成了一个CGLIB动态代理。

动态代理总结:动态代理具有静态代理的全部优点,其实可以说这种优点就是代理模式的优点,它能够使我们的程序再添加一些公共业务时能够更加的灵活,达到在不修改原始用户操作类代码的情况下,对其添加业务校验代码。而静态代理比较简单,不够灵活,适用面比较窄,实际的应用中使用的基本都是动态代理。Java中的动态代理实现包括JDK原生的动态代理和CGLIB动态代理。JDK动态代理必须要有接口,而CGLIB可以不要接口。两者相比较而言,JDK动态代理,它的适用性更高一点。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值