博客信息

代理模式proxy(结构模式)

发布时间:『 2019-03-22 04:36』  博客类别:23种设计模式  阅读(633)
  • 术语

    Proxy:代理

类图:

小李飞刀_设计模式


分类

  • 静态代理 角色 接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy 细节 代理对象与目标对象要实现相同的接口 调用的时候通过调用代理对象的方法来调用目标对象

案例代码如下:

package com.javaxl.design.proxy.staticproxy;

/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create  2020-02-24 9:35
* <p>
* 目标类
*/
public class TeacherDAO implements ITeacherDao {
  public void teach() {
      System.out.println("老师传授知识");
  }
}

//目标接口
interface ITeacherDao {
  void teach();
}

//代理类
class TeacherDAOProxy implements ITeacherDao {
  private ITeacherDao teacherDAO;

  public TeacherDAOProxy(ITeacherDao teacherDAO) {
      this.teacherDAO = teacherDAO;
  }

  @Override
  public void teach() {
      System.out.println("老师正式授课前的准备工作,如学生全部签到...");
      teacherDAO.teach();
      System.out.println("老师结束授课,如下课铃声响起...");
  }
}


public class Client {
  public static void main(String[] args) {
      TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO());
      proxy.teach();
  }
}

  • 动态代理

    • jdk代理 角色 接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy 细节 不需要实现接口,但是目标对象要实现接口,否则不能用动态代理 代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象 代理类所在包:java.lang.reflect.Proxy

    案例代码如下:

    package com.javaxl.design.proxy.dynamic.jdk;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.text.SimpleDateFormat;
    import java.util.Date;

    /**
    * 目标接口
    */
    interface ITeacherDao {
      String teach();

      ITeacherDao sleep(int minutes);
    }

    class TeacherDao implements ITeacherDao{
      @Override
      public String teach() {
          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
          return sdf.format(new Date())+":老师传授知识";
      }

      @Override
      public ITeacherDao sleep(int minutes) {
          System.out.println("老师睡了" + minutes + "分钟");
          return this;
      }

    }

    //真实代理类的外衣
    class TeacherDaoProxy{
      private ITeacherDao target;

      public TeacherDaoProxy(ITeacherDao target) {
          this.target = target;
      }

      public Object xxx(){
          return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                  target.getClass().getInterfaces(),
                  new InvocationHandler() {
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          Object obj = null;
                          String methodName = method.getName();
                          System.out.println("目标方法" + methodName + ":jdk代理开始...");
                          System.out.println("真实代理对象:"+proxy.getClass());
                          System.out.println("目标对象:"+target.getClass());
                          if("sleep".equals(methodName)){
    //                           method.invoke(target, args);
    //                           obj = proxy;
                              obj = method.invoke(target, args);
                          }else {
    //                       proxy是真实代理类,method是目标方法,args是目标方法携带的参数
                              obj = method.invoke(target, args);
                          }
                          System.out.println("目标方法" + methodName + ":jdk代理结束...");
                          return obj;
                      }
                  });
      }
    }


    public class Client {
      public static void main(String[] args) {
          TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
          ITeacherDao ins = (ITeacherDao) proxy.xxx();
          System.out.println("===========代理类实例被使用   begin=============");
          System.out.println(ins);
          System.out.println("===========代理类实例被使用   end=============");
          System.out.println(ins.teach());
    //       System.out.println(proxy.execute());
          System.out.println("===========代理类实例被使用   begin=============");
          ins.sleep(10);
          System.out.println("===========代理类实例被使用   end=============");
          ins.sleep(20).sleep(60);
      }
    }

    注意:java.lang.reflect.InvocationHandler.invoke第一个参数proxy的使用场景,链式编程中会用到;

小李飞刀_设计模式


小李飞刀_设计模式



      • Cglib代理 角色 接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy

        细节 目标对象与代理对象都不需要实现接口 Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展 Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用 Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类 注意: 1)需要引入 cglib 的 jar 文件 2)在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException: 3)目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

案例代码如下:

package com.javaxl.design.proxy.dynamic.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;


class TeacherDao {
  public String teach() {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      return sdf.format(new Date()) + ":老师传授知识";
  }

  public TeacherDao sleep(int minutes) {
      System.out.println("老师睡了" + minutes + "分钟");
      return this;
  }

}

//真实代理类的外衣
class TeacherDaoProxy implements MethodInterceptor {
  private Object target;

  public TeacherDaoProxy(Object target) {
      this.target = target;
  }

  //返回一个代理对象: 是 target 对象的代理对象
  public Object getProxyInstance() {
      //1. 创建一个工具类
      Enhancer enhancer = new Enhancer();
      //2. 设置父类
      enhancer.setSuperclass(target.getClass());
      //3. 设置回调函数
      enhancer.setCallback(this);
      //4. 创建子类对象,即代理对象
      return enhancer.create();
  }

  /**
    * @param proxyIns 由CGLib动态生成的代理类实例
    * @param method   上文中实体类所调用的被代理的方法引用
    * @param args     参数值列表
    * @param methodProxy   生成的代理类对方法的代理引用
    * @return
    * @throws Throwable
    */
  @Override
  public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
      String methodName = method.getName();
      Object res;
      System.out.println("目标方法" + methodName + ":cglib代理开始...");
      System.out.println("真实代理对象:" + proxyIns.getClass());
      System.out.println("目标对象:" + target.getClass());
      if ("sleep".equals(methodName)) {
//                           method.invoke(target, args);
//                           obj = proxy;
          res = method.invoke(target, args);
//           res = methodProxy.invokeSuper(proxyIns,args);
          res = proxyIns;
      } else {
//                       proxy是真实代理类,method是目标方法,args是目标方法携带的参数
          res = method.invoke(target, args);
      }
      System.out.println("目标方法" + methodName + ":cglib代理结束...");
      return res;
  }
}

public class Client {
  public static void main(String[] args) {
      TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
      proxy.sleep(111).sleep(222);
  }
}

jdk代理与Cglib代理比较:

JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高

使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

  • 应用

    Spring框架的AOP:Cglib代理的体现


总结

  代理模式是一种常用的模式,只在必要的时候生成实例,也分为很多种类,主要是按照使用去分类的,比如虚拟代理,以及其他的远程代理,访问控制代理等,我们主要要理解代理的实现本质,代理的意义,以及实际的用处。代理模式与装饰器模式比较类似,都是持有了同类或父类的引用(委托机制),并且在函数之中调用了同类的方法来加工与同类同名的本类的相应方法,但是也有区别,代理模式是为了减轻被代理人的工作,在不得已的时候再去打扰被代理人,而装饰器模式是为了产生新的功能,装饰原有的属性。


注意:代理模式与装饰者模式容易混淆。因为代理类与服务类都是实现统一接口。装饰模式也是如此,增强类和被增强类是实现同一接口。但是增强类的构造器中会传递被装饰类。

代理模式在spring aop中有充分的体现。


关键字:     设计模式  

备案号:湘ICP备19000029号

Copyright © 2018-2019 javaxl晓码阁 版权所有