Java设计模式——代理模式

Java设计模式——代理模式

前言

  什么是代理模式?

  定义:为其他对象提供一种代理以控制对这个对象的访问

   定义总是晦涩难懂的,举个栗子:买房子时的中介就是一个代理,中介的这种模式就是代理模式。
  为什么要使用代理模式?
   既然都有真实业务类的存在,为什么要"多此一举"的再找一个代理类来代理这个真实业务类来完成业务操作?(这就相当于是在问为什么不直接自己买房而需要找中介买房呢?)
   代理模式可以通过扩展代理类,进行一些功能的附加与增强
  代理模式分为静态代理与动态代理

一、静态代理

  两个子类共同实现同一个接口,其中一个子类负责真实业务实现,另一个子类完成辅助真实业务主题的操作(两个子类实现一个接口,一个类干事情,另一个类辅助它干事情)

   举个栗子
    以通过中介买房子为例:
     接口:买房子
     真实业务类:你(买房子)
     代理类:中介(帮买房子)

  静态代理的分析:静态代理指预先已经确定了代理与被代理者的关系了(买房子前你就已经和中介确定了"中介关系"),映射到编程中,就是指代理类与被代理类(真实业务类)在编译阶段就确定了依赖关系。
  定义一个代表买房子的接口
	interface HouseBuyer{
	    void buyHouse();
	}
  定义一个普通购房者类(真实业务类),实现HouseBuyer接口
	class Customer implements HouseBuyer{
	
	    @Override
	    public void buyHouse() {
	        System.out.println("在银行付钱买房");
	    }
	}
  定义一个中介类(代理类),除了实现HouseBuyer接口中已定义的方法外
	class Agent implements HouseBuyer{
	
		// 代理类中持有被代理类的对象
	    HouseBuyer houseBuyer;
		
		// 代理类通过构造方法将被代理类的对象传入
	    public Agent(HouseBuyer houseBuyer) {
	        this.houseBuyer = houseBuyer;
	    }
	
	    public void beforeBuyHouse(){
	        System.out.println("买房子前带着购房者坐车去游览小区再坐车去银行");
	    }
	
		// 代理类实现接口的方法==调用被代理类实现该接口的方法(真实业务)
    	//          +代理类中定义的其他用于辅助真实业务操作的方法(辅助方法)
	    @Override
	    public void buyHouse() {
	        beforeBuyHouse();
	        this.houseBuyer.buyHouse();
	        afterBuyHouse();
	    }
	
	    public void afterBuyHouse(){
	        System.out.println("买房子后将购房者送至想去的位置");
	    }
	}
  定义一个中介工厂,用户返回中介类的对象
	class ProxyFactory{
	    // 工厂方法一般都是静态的
	    public static HouseBuyer getProxy(){
	        return new Agent(new Customer());
	    }
	}
  在客户端使用代理对象来进行相关操作
	 public static void main(String[] args) {
	        HouseBuyer houseBuyer = ProxyFactory.getProxy();
	        houseBuyer.buyHouse();
	 }
  打印结果如下:

在这里插入图片描述

  小小的总结一下静态代理:通过代理进行相关功能的增强,代理类中引用被代理类的对象(通过构造方法),并且代理类在实现接口方法时不直接进行操作,而是调用被代理类实现接口的方法(真实业务)+在代理类中定义的其他用于辅助真实业务类操作的方法(辅助方法)。还是中介的这个例子,购房者通过中介的"代理"购买房子,中介为购房者提供购房前,购房后的服务(车接车送),增强了购房者购买房子的功能。

二、动态代理

  动态代理的引入:动态代理仍然是代理,情况与静态代理完全一样,只是代理与被代理人的关系是动态确定的(买房的当天才直接找了一个中介),映射到编程中,就是指代理类与被代理类(真实业务类)在运行阶段确定了依赖关系。
  为什么要使用动态代理(动态代理并没有在静态代理的基础上增强任何代理方面的任何功能)?
   动态代理具有灵活性、强扩展性的优势,若使用静态代理,若还有一个购房者需要购房,就得再创建一个新的中介(代理类),而若使用动态代理,则只需要创建一个中介(代理类),将很多的购房者都交给这个中介来处理。
  动态的创建代理类进行代理(思路与静态代理基本一样)
  定义2(N)个普通购房者类(真实业务类),实现HouseBuyer接口
	// 购房者B
	class CustomerB implements HouseBuyer{
	    @Override
	    public void buyHouse() {
	        System.out.println("大家好,我是喜欢唱、跳、Rap、篮球的B,我在银行付钱买房");
	    }
	}
	
	// 购房者C
	class CustomerC implements HouseBuyer{
	    @Override
	    public void buyHouse() {
	        System.out.println("大家好,我是喜欢唱、跳、Rap、篮球的C,我在银行付钱买房");
	    }
	}
  定义一个中介类,此时该中介类不需要实现买房子的接口,而是实现InvocationHandler接口(中的invoke()方法)
	class DynProxyAgent implements InvocationHandler{
	
	    // 代理类中持有被代理类的对象
	    Object houseBuyer;
	
	    // 代理类通过构造方法将被代理类的对象传入
	    public DynProxyAgent(Object houseBuyer) {
	        this.houseBuyer = houseBuyer;
	    }
	
	    public void beforeBuyHouse(){
	        System.out.println("买房子前带着购房者坐车去游览小区再坐车去银行");
	    }
	
	    public void afterBuyHouse(){
	        System.out.println("买房子后将购房者送至想去的位置");
	    }
	
		// 无论调用被代理者中的哪个方法,都需要先进入代理类的invoke(),代理新增的逻辑就写在invoke()中
	    @Override
	    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	        this.beforeBuyHouse();
	        Object ret = method.invoke(this.houseBuyer,args);
	        this.afterBuyHouse();
	        return ret;
	    }
	}
  改造一下静态代理工厂,使用Proxy.newProxyInstance()由系统动态的创建一个代理类。
	public static Object getProxy(Object target){
			// 先获取动态代理类实例(实现了InvocationHandler)
	        InvocationHandler handler = new DynProxyAgent(target);
	        // 返回由Proxy.newProxyInstance()创建的动态代理类
	        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
	                target.getClass().getInterfaces(),handler);
	}
  在客户端使用代理对象来进行相关操作
	public static void main(String[] args) {
	        HouseBuyer houseBuyerB = (HouseBuyer) DynProxyFactory.getProxy(new CustomerB());
	        houseBuyerB.buyHouse();
	        System.out.println("=============================================");
	        HouseBuyer houseBuyerC = (HouseBuyer) DynProxyFactory.getProxy(new CustomerC());
	        houseBuyerC.buyHouse();
	}
  打印结果如下(注意不同购房者B和C的区别):

在这里插入图片描述

  小小的总结一下动态代理:动态代理的本质和静态代理是一样的,都是在代理类中对实际业务类进行了功能上的扩展,只不过,动态代理产生的代理类是在运行时由系统产生,代替了静态代理一个一个创建代理类的方法,动态代理类实现InvocationHandler接口中的invoke()方法。
  InvocationHandler接口的作用:当代理对象(Proxy.newProxyInstance产生的对象)的原本方法被调用时,不会执行原本的方法,而是会执行InvocationHandler中的invoke()方法,代理对象代理的逻辑全部写在这个invoke()方法中即可。
  动态代理的流程图
   使用动态代理工厂生成动态代理类proxy->调用proxy中的方法->调用newProxyInstance的参数h中的invoke()方法->根据invoke()方法中的逻辑调用其他方法->完成动态代理。
   ①动态的生成一个代理对象——通过Proxy.newProxyInstance()

在这里插入图片描述

    loader:被代理的类的类加载器
    interfaces:被代理的类所实现的接口(可以有多个)
    h:调用当前代理对象的方法时,不直接调用当前代理对象的方法,而是调用h(实现了InvocationHandler接口的"代理类")中的invoke()方法
   创建好代理类proxy后该方法会调用h中的invoke()方法
   ②实现了InvocationHandler的"代理类"

在这里插入图片描述

    proxy:代理后的实例对象
    method:代理对象被调用方法
    args:被调用时的参数
   invoke()方法除了会调用代理类中为增加功能而实现的其他方法(辅助方法),还会通过反射(Method.invoke())调用被代理类中实现接口的方法(真实业务)。
  1、JDK代理
   JDK的动态代理方法是依赖于接口的,首先使用接口来定义好操作的规范,然后通过Proxy类产生的代理对象调用被代理对象的操作,而这个操作又被分发给InvocationHandler接口的invoke()方法具体执行。
   上面的例子即为JDK代理,使用Proxy类newProxyInstance()方法生成的代理对象houseBuyerB去调用了houseBuyerB.buyHouse();操作,此时,系统将此方法分发给invoke()方法,其中houseBuyerB是系统帮我们动态生成的,同样实现了接口HouseBuyer
  2、cgLib代理
   JDK实现的动态代理只能针对实现了接口的类,而不能对没实现接口的类进行动态代理,因此引入cgLib(Code Generation Library),底层使用java字节码操作框架ASM实现。
   首先要使用cgLib,需要引入cgLib的库(jar包)。点击后面这个链接可以直接下载的哦。cglib-nodep-3.2.6.jar
   举个栗子说明cgLib的执行流程,由于动态代理主要使用JDK代理,这里就不对cgLib代理的细节与详情展开讨论了。
   定义一个真实业务类
	class Message{
	    public void send(){
	        System.out.println("send a message");
	    }
	}
   定义中间代理类,实现MethodInterceptor接口(中的intercept()方法)——大体流程与JDK代理的实现InvocationHandler类似。
	class ClassProxy implements MethodInterceptor{
	
	    // 接收真实业务类
	    private Object target;
	
	    public ClassProxy(Object target) {
	        this.target = target;
	    }
	
	    private void beforeMethod(){
	        System.out.println("before send");
	    }
	
	    private void afterMethod(){
	        System.out.println("after send");
	    }
	
	    @Override
	    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
	          beforeMethod();
	          Object ret = method.invoke(this.target,objects);
	          afterMethod();
	          return ret;
	    }
	}
   与JDK类似,CgLib的工厂类也需要创建Enhancer对象,并对该对象进行一系列操作。
	class CgLibFactory{
	    public static Object getCgLib(Object obj){
	        // 负责代理对象的代理处理类
	        Enhancer enhancer = new Enhancer();
	        enhancer.setSuperclass(obj.getClass());
	        // 代理对象,以上就动态配置好了类之间的代理关系
	        enhancer.setCallback(new ClassProxy(obj));
	        return enhancer.create();
	    }
	}
   在客户端使用代理对象来进行相关操作
	public static void main(String[] args) {
	        Message msg = new Message();
	        Message tmp = (Message) CgLibFactory.getCgLib(msg);
	        tmp.send();
	}
   打印结果如下:

在这里插入图片描述

   小小总结一下,cgLib的主体流程与JDK十分相似,都是代理中间类实现一个接口,Java提供一种动态创建代理类的方式,经过一系列的操作,在运行时动态生成代理类,由代理类增强被代理类的功能。cgLib具体的流程,参考JDK代理阅读源码即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值