• cglib用法


    CGLib动态代理的介绍及用法(单回调、多回调、不处理、固定值、懒加载)

    参照: https://blog.csdn.net/difffate/article/details/70552056

    前面介绍了代理模式,讲了动态代理常见的实现方式,包含了JDK的动态代理和CGLib的动态代理。本文将介绍下CGLib动态代理及几种用法CGLib(Code Generation Library)是一个高效的代码生成库,底层实现是使用asm来转换字节码生成类。在生成代理类的场景中,由于JDK动态代理必须要求源对象有实现接口,而实际场景中,并不是所有类都有实现接口,因此使用CGLib可以用在未实现接口的类上。

    值得注意几点是:

    1)使用CGLib代理的类不能是final修饰的,因为代理类需要继承主题类;

    2)final修饰的方法不会被切入;

    3)如果主题类的构造函数不是默认空参数的,那么在使用Enhancer类create的时候,选择create(java.lang.Class[] argumentTypes, java.lang.Object[] arguments) 方法。

    接下来认识实现动态代理最重要的一个接口 MethodInteceptor

     

    1.  
      package net.sf.cglib.proxy;
    2.  
       
    3.  
      /**
    4.  
      * General-purpose {@link Enhancer} callback which provides for "around advice".
    5.  
      * @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
    6.  
      * @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
    7.  
      */
    8.  
      public interface MethodInterceptor
    9.  
      extends Callback
    10.  
      {
    11.  
      /**
    12.  
      * All generated proxied methods call this method instead of the original method.
    13.  
      * The original method may either be invoked by normal reflection using the Method object,
    14.  
      * or by using the MethodProxy (faster).
    15.  
      * @param obj "this", the enhanced object
    16.  
      * @param method intercepted Method
    17.  
      * @param args argument array; primitive types are wrapped
    18.  
      * @param proxy used to invoke super (non-intercepted method); may be called
    19.  
      * as many times as needed
    20.  
      * @throws Throwable any exception may be thrown; if so, super method will not be invoked
    21.  
      * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
    22.  
      * @see MethodProxy
    23.  
      */
    24.  
      public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
    25.  
      MethodProxy proxy) throws Throwable;
    26.  
       
    27.  
      }


    MethodInterceptor,从名字上方法拦截器,就是对方法做切入的。intercept方式的4个参数分别对应增强对象、调用方法、方法参数以及调用父类方法的代理。使用MethodProxy速度会更快,所以后面将用

     

    下面介绍几种用法,这里使用spring包中cglib,其实和引单独的cglib包是一样,只不过spring为了版本不冲突,将cglib包含在自己的包中。

    先定义一个主题对象

     

    1.  
      /**
    2.  
      * Create by zxb on 2017/4/23
    3.  
      */
    4.  
      public class DBQuery {
    5.  
       
    6.  
      public DBQuery() {
    7.  
      }
    8.  
       
    9.  
      public DBQuery(Integer i) {
    10.  
      System.out.println("Here's in DBQuery Constructor");
    11.  
      }
    12.  
       
    13.  
      public String getElement(String id) {
    14.  
      return id + "_CGLib";
    15.  
      }
    16.  
       
    17.  
      public List<String> getAllElements() {
    18.  
      return Arrays.asList("Hello_CGLib1", "Hello_CGLib2");
    19.  
      }
    20.  
       
    21.  
      public String methodForNoop() {
    22.  
      return "Hello_Noop";
    23.  
      }
    24.  
       
    25.  
      public String methodForFixedValue(String param) {
    26.  
      return "Hello_" + param;
    27.  
      }
    28.  
       
    29.  
      public final String sayHello() {
    30.  
      return "Hello Everyone!";
    31.  
      }
    32.  
      }


    (一)单回调

    切入类:

     

    1.  
      /**
    2.  
      * Create by zxb on 2017/4/22
    3.  
      */
    4.  
      public class DBQueryProxy implements MethodInterceptor {
    5.  
       
    6.  
      public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    7.  
      System.out.println("Here in interceptor !");
    8.  
      return methodProxy.invokeSuper(o, objects);
    9.  
      }
    10.  
      }

    测试类:

     

    1.  
      public class TestCGLibProxy {
    2.  
       
    3.  
      public static void main(String[] args) {
    4.  
      DBQueryProxy dbQueryProxy = new DBQueryProxy();
    5.  
      Enhancer enhancer = new Enhancer();
    6.  
      enhancer.setSuperclass(DBQuery.class);
    7.  
      enhancer.setCallback(dbQueryProxy);
    8.  
      // DBQuery dbQuery = (DBQuery)enhancer.create(new Class[]{Integer.class}, new Object[]{1});
    9.  
      DBQuery dbQuery = (DBQuery) enhancer.create();
    10.  
      System.out.println(dbQuery.getElement("Hello"));
    11.  
      System.out.println();
    12.  
      System.out.println(dbQuery.getAllElements());
    13.  
      System.out.println();
    14.  
      System.out.println(dbQuery.sayHello());
    15.  
      }
    16.  
      }


    执行结果:


    (二)多回调

    在前面的基础上,加个切入类,并通过CallbackFilter来决定是使用哪个切入类

     

    1.  
      /**
    2.  
      * Create by zxb on 2017/4/22
    3.  
      */
    4.  
      public class DBQueryProxy2 implements MethodInterceptor {
    5.  
       
    6.  
      public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    7.  
      System.out.println("Here in interceptor 2!");
    8.  
      return methodProxy.invokeSuper(o, objects);
    9.  
      }
    10.  
      }

    测试类:

     

    1.  
      /**
    2.  
      * Create by zxb on 2017/4/22
    3.  
      */
    4.  
      public class TestCGLibProxy {
    5.  
       
    6.  
      public static void main(String[] args) {
    7.  
      DBQueryProxy dbQueryProxy = new DBQueryProxy();
    8.  
      DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
    9.  
      Enhancer enhancer = new Enhancer();
    10.  
      enhancer.setSuperclass(DBQuery.class);
    11.  
      enhancer.setCallbacks(new Callback[]{dbQueryProxy, dbQueryProxy2});
    12.  
      enhancer.setCallbackFilter(new CallbackFilter() {
    13.  
       
    14.  
      public int accept(Method method) {
    15.  
      if (method.getName().equals("getElement")) {
    16.  
      return 0;
    17.  
      } else {
    18.  
      return 1;
    19.  
      }
    20.  
      }
    21.  
      });
    22.  
      DBQuery dbQuery = (DBQuery) enhancer.create();
    23.  
      System.out.println("========Inteceptor By DBQueryProxy ========");
    24.  
      System.out.println(dbQuery.getElement("Hello"));
    25.  
      System.out.println();
    26.  
      System.out.println("========Inteceptor By DBQueryProxy2 ========");
    27.  
      System.out.println(dbQuery.getAllElements());
    28.  
      }
    29.  
      }


    执行结果:


    (三)不处理

    利用枚举常量 Callback noopCb = NoOp.INSTANCE;

     

    测试类:

     

    1.  
      public class TestCGLibProxy {
    2.  
       
    3.  
      public static void main(String[] args) {
    4.  
      DBQueryProxy dbQueryProxy = new DBQueryProxy();
    5.  
      DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
    6.  
      Callback noopCb = NoOp.INSTANCE;
    7.  
      Enhancer enhancer = new Enhancer();
    8.  
      enhancer.setSuperclass(DBQuery.class);
    9.  
      enhancer.setCallbacks(new Callback[]{dbQueryProxy, dbQueryProxy2, noopCb});
    10.  
      enhancer.setCallbackFilter(new CallbackFilter() {
    11.  
       
    12.  
      public int accept(Method method) {
    13.  
      if (method.getName().equals("getElement")) {
    14.  
      return 0;
    15.  
      } else if (method.getName().equals("getAllElements")) {
    16.  
      return 1;
    17.  
      } else {
    18.  
      return 2;
    19.  
      }
    20.  
      }
    21.  
      });
    22.  
      DBQuery dbQuery = (DBQuery) enhancer.create();
    23.  
      System.out.println("========Inteceptor By DBQueryProxy ========");
    24.  
      System.out.println(dbQuery.getElement("Hello"));
    25.  
      System.out.println();
    26.  
      System.out.println("========Inteceptor By DBQueryProxy2 ========");
    27.  
      System.out.println(dbQuery.getAllElements());
    28.  
      System.out.println();
    29.  
      System.out.println("========Return Original Value========");
    30.  
      System.out.println(dbQuery.methodForNoop());
    31.  
      }
    32.  
      }


    执行结果:


    (四)固定值

    需要实现FixedValue接口,会忽略原来函数的返回值,使用固定值来替换。

     

    1.  
      /**
    2.  
      * 返回固定的值
    3.  
      * Create by zxb on 2017/4/23
    4.  
      */
    5.  
      public class DBQueryProxyFixedValue implements FixedValue {
    6.  
       
    7.  
      public Object loadObject() throws Exception {
    8.  
      System.out.println("Here in DBQueryProxyFixedValue ! ");
    9.  
      return "Fixed Value";
    10.  
      }
    11.  
      }

    测试类:

     

    1.  
      public class TestCGLibProxy {
    2.  
       
    3.  
      public static void main(String[] args) {
    4.  
      DBQueryProxy dbQueryProxy = new DBQueryProxy();
    5.  
      DBQueryProxy2 dbQueryProxy2 = new DBQueryProxy2();
    6.  
      Callback noopCb = NoOp.INSTANCE;
    7.  
      Callback fixedValue = new DBQueryProxyFixedValue();
    8.  
      Enhancer enhancer = new Enhancer();
    9.  
      enhancer.setSuperclass(DBQuery.class);
    10.  
      enhancer.setCallbacks(new Callback[]{dbQueryProxy, dbQueryProxy2, noopCb, fixedValue});
    11.  
      enhancer.setCallbackFilter(new CallbackFilter() {
    12.  
       
    13.  
      public int accept(Method method) {
    14.  
      if (method.getName().equals("getElement")) {
    15.  
      return 0;
    16.  
      } else if (method.getName().equals("getAllElements")) {
    17.  
      return 1;
    18.  
      } else if (method.getName().equals("methodForNoop")) {
    19.  
      return 2;
    20.  
      } else if (method.getName().equals("methodForFixedValue")) {
    21.  
      return 3;
    22.  
      } else {
    23.  
      return 0;
    24.  
      }
    25.  
      }
    26.  
      });
    27.  
      DBQuery dbQuery = (DBQuery) enhancer.create();
    28.  
      System.out.println("========Inteceptor By DBQueryProxy ========");
    29.  
      System.out.println(dbQuery.getElement("Hello"));
    30.  
      System.out.println();
    31.  
      System.out.println("========Inteceptor By DBQueryProxy2 ========");
    32.  
      System.out.println(dbQuery.getAllElements());
    33.  
      System.out.println();
    34.  
      System.out.println("========Return Original Value========");
    35.  
      System.out.println(dbQuery.methodForNoop());
    36.  
      System.out.println();
    37.  
      System.out.println("========Return Fixed Value========");
    38.  
      System.out.println(dbQuery.methodForFixedValue("myvalue"));
    39.  
      }
    40.  
      }

    执行结果:


    (五)懒加载

    CGLib的懒加载,可以用在一些不需要立即加载完整对象实例的场景,比如说Hibernate中的查询对象,如果这个对象有关联其他对象,这个时候不会马上将关联对象一起查询出来关联,要等到调用到这个关联对象时才去做查询。利用CGLib的懒加载机制,可以很好的实现这个需求。需要了解2个接口,LazyLoader和Dispatcher。这两个接口的定义如下:

     

    1.  
      public interface LazyLoader extends Callback {
    2.  
      Object loadObject() throws Exception;
    3.  
      }
    4.  
       
    5.  
      public interface Dispatcher extends Callback {
    6.  
      Object loadObject() throws Exception;
    7.  
      }


    它们都继承了Callback接口,都有一个loadObject的方法,区别在于LazyLoader只有在第一次调用时,会执行loadObject获取对象,而Dispatcher会在每次调用时都触发loadObject方法,不理解?没关系,后面代码示例上可以看到明显的区别。假定有个学生类(Student),学术类包含2门课的课程表对象,分别是英语课程表(EnglishSchedule)和数学课程表(MathSchedule),它们都是课程表类的实例

     

    1.  
      /**
    2.  
      * 课程表
    3.  
      * Create by zxb on 2017/4/23
    4.  
      */
    5.  
      @Data
    6.  
      @NoArgsConstructor
    7.  
      @AllArgsConstructor
    8.  
      public class Schedule {
    9.  
       
    10.  
      private String courseName;
    11.  
       
    12.  
      private Date courseTime;
    13.  
      }

    EnglishSchedule属性的加载依赖于ScheduleLazyLoader

     

    1.  
      /**
    2.  
      * Create by zxb on 2017/4/23
    3.  
      */
    4.  
      public class ScheduleLazyLoader implements LazyLoader {
    5.  
       
    6.  
      public Object loadObject() throws Exception {
    7.  
      System.out.println("before LazyLoader init...you can query from db...");
    8.  
      Schedule schedule = new Schedule();
    9.  
      schedule.setCourseName("English");
    10.  
      Calendar calendar = Calendar.getInstance();
    11.  
      calendar.set(2017,3,28);
    12.  
      schedule.setCourseTime(calendar.getTime());
    13.  
      System.out.println("after LazyLoader init...");
    14.  
      return schedule;
    15.  
      }
    16.  
      }


    MathSchedule属性的加载依赖于ScheduleDispatcher

     

    1.  
      /**
    2.  
      * Create by zxb on 2017/4/23
    3.  
      */
    4.  
      public class ScheduleDispatcher implements Dispatcher {
    5.  
       
    6.  
      public Object loadObject() throws Exception {
    7.  
      System.out.println("before Dispatcher init...you can query from db...");
    8.  
      Schedule schedule = new Schedule();
    9.  
      schedule.setCourseName("Math");
    10.  
      Calendar calendar = Calendar.getInstance();
    11.  
      calendar.set(2017,4,1);
    12.  
      schedule.setCourseTime(calendar.getTime());
    13.  
      System.out.println("after Dispatcher init...");
    14.  
      return schedule;
    15.  
      }
    16.  
      }


    学生类:

    定义时,需要对EnglishSchedule和MathSchedule先初始为动态代理的对象

     

    1.  
      package org.zheng.proxy.cglib.lazyload;
    2.  
       
    3.  
      import lombok.Data;
    4.  
      import org.springframework.cglib.proxy.Enhancer;
    5.  
       
    6.  
      /**
    7.  
      * Create by zxb on 2017/4/23
    8.  
      */
    9.  
      @Data
    10.  
      public class Student {
    11.  
       
    12.  
      private int id;
    13.  
       
    14.  
      private String name;
    15.  
       
    16.  
      /**
    17.  
      * 英语课时间表
    18.  
      */
    19.  
      private Schedule EnglishSchedule;
    20.  
       
    21.  
      /**
    22.  
      * 数学课时间表
    23.  
      */
    24.  
      private Schedule MathSchedule;
    25.  
       
    26.  
      public Student(int id, String name) {
    27.  
      this.id = id;
    28.  
      this.name = name;
    29.  
      this.EnglishSchedule = createEnglishSchedule();
    30.  
      this.MathSchedule = createMathSchedule();
    31.  
      }
    32.  
       
    33.  
      private Schedule createEnglishSchedule() {
    34.  
      Enhancer enhancer = new Enhancer();
    35.  
      enhancer.setSuperclass(Schedule.class);
    36.  
      enhancer.setCallback(new ScheduleLazyLoader());
    37.  
      return (Schedule) enhancer.create();
    38.  
      }
    39.  
       
    40.  
      private Schedule createMathSchedule() {
    41.  
      Enhancer enhancer = new Enhancer();
    42.  
      enhancer.setSuperclass(Schedule.class);
    43.  
      enhancer.setCallback(new ScheduleDispatcher());
    44.  
      return (Schedule) enhancer.create();
    45.  
      }
    46.  
      }


    测试类:

     

    1.  
      /**
    2.  
      * 延迟加载属性
    3.  
      * Create by zxb on 2017/4/23
    4.  
      */
    5.  
      public class LazyLoadTest {
    6.  
       
    7.  
      public static void main(String[] args) {
    8.  
      Student student = new Student(666, "XiaoMing");
    9.  
      System.out.println("id=" + student.getId());
    10.  
      System.out.println("name=" + student.getName());
    11.  
      // LazyLoader 只有第一次,Dispatcher是每次都会进loadObject的方法
    12.  
      System.out.println("========First Get EnglishSchedule ========");
    13.  
      System.out.println(student.getEnglishSchedule());
    14.  
      System.out.println();
    15.  
      System.out.println("========First Get MathSchedule ========");
    16.  
      System.out.println(student.getMathSchedule());
    17.  
      System.out.println();
    18.  
      System.out.println("========Second Get EnglishSchedule ========");
    19.  
      System.out.println(student.getEnglishSchedule());
    20.  
      System.out.println();
    21.  
      System.out.println("========Second Get MathSchedule ========");
    22.  
      System.out.println(student.getMathSchedule());
    23.  
      }
    24.  
      }


    执行结果:


    可以看到第二次取懒加载对象的时候,实现LoadLazy接口不会重新执行loadObject,而实现Dispatcher的会重新执行LoadObject方法:)

     

    以上,就是使用CGLib的几种常见用法。

     

    项目完整代码:

    https://github.com/difffate/JavaProject

  • 相关阅读:
    Arch 真好用
    Spring 自定义注解-字段注解
    Raft论文概述
    Raft成员变化(Membership Change)
    Reactor模式详解
    高性能IO之Reactor模式
    WinFrm中多线程操作窗体属性
    Reactor模式
    高并发中的线程与线程池
    二层交换机与三层交换机区别详解!
  • 原文地址:https://www.cnblogs.com/z-test/p/9549444.html
Copyright © 2020-2023  润新知