一、什么是CGLIB?

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

二、CGLIB原理

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法,无法进行代理。

三、CGLIB的应用

广泛的被许多AOP的框架使用,例如Spring AOP和dynaop。Hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联。

四、CGLIB的API

1、Jar包

cglib-nodep-2.2.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.

cglib-2.2.jar:使用此jar包需要关联asm的jar包,否则运行时报错.

2、CGLIB类库:

由于基本代码很少,学起来有一定的困难,主要是缺少文档和示例,这也是CGLIB的一个不足之处

本系列使用的CGLIB版本是2.2

net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系。

net.sf.cglib.transform:编译期或运行期类和类文件的转换

net.sf.cglib.proxy:实现创建代理和方法拦截器的类

net.sf.cglib.reflect:实现快速反射和C#风格代理的类

net.sf.cglib.util:集合排序等工具类

net.sf.cglib.beans:JavaBean相关的工具类

本篇介绍通过MethodInterceptor和Enhancer实现一个动态代理。

一、首先说一下JDK中的动态代理

JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,

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

二、使用CGLib实现

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

下面,将通过一个实例介绍使用CGLib实现动态代理。

1、被代理类

首先,定义一个类,该类没有实现任何接口

  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. /**
  3. * 没有实现接口,需要CGlib动态代理的目标类
  4. *
  5. * @author zghw
  6. *
  7. */
  8. public class TargetObject {
  9. public String method1(String paramName) {
  10. return paramName;
  11. }
  12. public int method2(int count) {
  13. return count;
  14. }
  15. public int method3(int count) {
  16. return count;
  17. }
  18. @Override
  19. public String toString() {
  20. return "TargetObject []"+ getClass();
  21. }
  22. }</span>

2、拦截器

定义一个拦截器。在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。

  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import java.lang.reflect.Method;
  3. import net.sf.cglib.proxy.MethodInterceptor;
  4. import net.sf.cglib.proxy.MethodProxy;
  5. /**
  6. * 目标对象拦截器,实现MethodInterceptor
  7. * @author zghw
  8. *
  9. */
  10. public class TargetInterceptor implements MethodInterceptor{
  11. /**
  12. * 重写方法拦截在方法前和方法后加入业务
  13. * Object obj为目标对象
  14. * Method method为目标方法
  15. * Object[] params 为参数,
  16. * MethodProxy proxy CGlib方法代理对象
  17. */
  18. @Override
  19. public Object intercept(Object obj, Method method, Object[] params,
  20. MethodProxy proxy) throws Throwable {
  21. System.out.println("调用前");
  22. Object result = proxy.invokeSuper(obj, params);
  23. System.out.println(" 调用后"+result);
  24. return result;
  25. }
  26. }
  27. </span>

参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

返回:从代理实例的方法调用返回的值。

其中,proxy.invokeSuper(obj,arg):

调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)

在这个示例中,只在调用被代理类方法前后各打印了一句话,当然实际编程中可以是其它复杂逻辑。

3、生成动态代理类

  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import net.sf.cglib.proxy.Callback;
  3. import net.sf.cglib.proxy.CallbackFilter;
  4. import net.sf.cglib.proxy.Enhancer;
  5. import net.sf.cglib.proxy.NoOp;
  6. public class TestCglib {
  7. public static void main(String args[]) {
  8. Enhancer enhancer =new Enhancer();
  9. enhancer.setSuperclass(TargetObject.class);
  10. enhancer.setCallback(new TargetInterceptor());
  11. TargetObject targetObject2=(TargetObject)enhancer.create();
  12. System.out.println(targetObject2);
  13. System.out.println(targetObject2.method1("mmm1"));
  14. System.out.println(targetObject2.method2(100));
  15. System.out.println(targetObject2.method3(200));
  16. }
  17. }
  18. </span>

这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型TargetObject。

最后,在代理类上调用方法.

4、回调过滤器CallbackFilter

一、作用

在CGLib回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。

在JDK动态代理中并没有类似的功能,对InvocationHandler接口方法的调用对代理类内的所以方法都有效。

定义实现过滤器CallbackFilter接口的类:

  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import java.lang.reflect.Method;
  3. import net.sf.cglib.proxy.CallbackFilter;
  4. /**
  5. * 回调方法过滤
  6. * @author zghw
  7. *
  8. */
  9. public class TargetMethodCallbackFilter implements CallbackFilter {
  10. /**
  11. * 过滤方法
  12. * 返回的值为数字,代表了Callback数组中的索引位置,要到用的Callback
  13. */
  14. @Override
  15. public int accept(Method method) {
  16. if(method.getName().equals("method1")){
  17. System.out.println("filter method1 ==0");
  18. return 0;
  19. }
  20. if(method.getName().equals("method2")){
  21. System.out.println("filter method2 ==1");
  22. return 1;
  23. }
  24. if(method.getName().equals("method3")){
  25. System.out.println("filter method3 ==2");
  26. return 2;
  27. }
  28. return 0;
  29. }
  30. }
  31. </span>

其中return值为被代理类的各个方法在回调数组Callback[]中的位置索引(见下文)。

  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import net.sf.cglib.proxy.Callback;
  3. import net.sf.cglib.proxy.CallbackFilter;
  4. import net.sf.cglib.proxy.Enhancer;
  5. import net.sf.cglib.proxy.NoOp;
  6. public class TestCglib {
  7. public static void main(String args[]) {
  8. Enhancer enhancer =new Enhancer();
  9. enhancer.setSuperclass(TargetObject.class);
  10. CallbackFilter callbackFilter = new TargetMethodCallbackFilter();
  11. /**
  12. * (1)callback1:方法拦截器
  13. (2)NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。
  14. (3)FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
  15. */
  16. Callback noopCb=NoOp.INSTANCE;
  17. Callback callback1=new TargetInterceptor();
  18. Callback fixedValue=new TargetResultFixed();
  19. Callback[] cbarray=new Callback[]{callback1,noopCb,fixedValue};
  20. //enhancer.setCallback(new TargetInterceptor());
  21. enhancer.setCallbacks(cbarray);
  22. enhancer.setCallbackFilter(callbackFilter);
  23. TargetObject targetObject2=(TargetObject)enhancer.create();
  24. System.out.println(targetObject2);
  25. System.out.println(targetObject2.method1("mmm1"));
  26. System.out.println(targetObject2.method2(100));
  27. System.out.println(targetObject2.method3(100));
  28. System.out.println(targetObject2.method3(200));
  29. }
  30. }
  31. </span>
  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import net.sf.cglib.proxy.FixedValue;
  3. /**
  4. * 表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
  5. * @author zghw
  6. *
  7. */
  8. public class TargetResultFixed implements FixedValue{
  9. /**
  10. * 该类实现FixedValue接口,同时锁定回调值为999
  11. * (整型,CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue,该方法返回值为整型)。
  12. */
  13. @Override
  14. public Object loadObject() throws Exception {
  15. System.out.println("锁定结果");
  16. Object obj = 999;
  17. return obj;
  18. }
  19. }
  20. </span>

5.延迟加载对象

一、作用:
说到延迟加载,应该经常接触到,尤其是使用Hibernate的时候,本篇将通过一个实例分析延迟加载的实现方式。
LazyLoader接口继承了Callback,因此也算是CGLib中的一种Callback类型。

另一种延迟加载接口Dispatcher。

Dispatcher接口同样继承于Callback,也是一种回调类型。

但是Dispatcher和LazyLoader的区别在于:LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法,而Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法。

二、示例:
首先定义一个实体类LoaderBean,该Bean内有一个需要延迟加载的属性PropertyBean。

  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import net.sf.cglib.proxy.Enhancer;
  3. public class LazyBean {
  4. private String name;
  5. private int age;
  6. private PropertyBean propertyBean;
  7. private PropertyBean propertyBeanDispatcher;
  8. public LazyBean(String name, int age) {
  9. System.out.println("lazy bean init");
  10. this.name = name;
  11. this.age = age;
  12. this.propertyBean = createPropertyBean();
  13. this.propertyBeanDispatcher = createPropertyBeanDispatcher();
  14. }
  15. /**
  16. * 只第一次懒加载
  17. * @return
  18. */
  19. private PropertyBean createPropertyBean() {
  20. /**
  21. * 使用cglib进行懒加载 对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。
  22. * 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法,
  23. * 就会自动触发代理类回调)。
  24. */
  25. Enhancer enhancer = new Enhancer();
  26. enhancer.setSuperclass(PropertyBean.class);
  27. PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class,
  28. new ConcreteClassLazyLoader());
  29. return pb;
  30. }
  31. /**
  32. * 每次都懒加载
  33. * @return
  34. */
  35. private PropertyBean createPropertyBeanDispatcher() {
  36. Enhancer enhancer = new Enhancer();
  37. enhancer.setSuperclass(PropertyBean.class);
  38. PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class,
  39. new ConcreteClassDispatcher());
  40. return pb;
  41. }
  42. public String getName() {
  43. return name;
  44. }
  45. public void setName(String name) {
  46. this.name = name;
  47. }
  48. public int getAge() {
  49. return age;
  50. }
  51. public void setAge(int age) {
  52. this.age = age;
  53. }
  54. public PropertyBean getPropertyBean() {
  55. return propertyBean;
  56. }
  57. public void setPropertyBean(PropertyBean propertyBean) {
  58. this.propertyBean = propertyBean;
  59. }
  60. public PropertyBean getPropertyBeanDispatcher() {
  61. return propertyBeanDispatcher;
  62. }
  63. public void setPropertyBeanDispatcher(PropertyBean propertyBeanDispatcher) {
  64. this.propertyBeanDispatcher = propertyBeanDispatcher;
  65. }
  66. @Override
  67. public String toString() {
  68. return "LazyBean [name=" + name + ", age=" + age + ", propertyBean="
  69. + propertyBean + "]";
  70. }
  71. }
  72. </span>
  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. public class PropertyBean {
  3. private String key;
  4. private Object value;
  5. public String getKey() {
  6. return key;
  7. }
  8. public void setKey(String key) {
  9. this.key = key;
  10. }
  11. public Object getValue() {
  12. return value;
  13. }
  14. public void setValue(Object value) {
  15. this.value = value;
  16. }
  17. @Override
  18. public String toString() {
  19. return "PropertyBean [key=" + key + ", value=" + value + "]" +getClass();
  20. }
  21. }
  22. </span>
  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import net.sf.cglib.proxy.LazyLoader;
  3. public class ConcreteClassLazyLoader implements LazyLoader {
  4. /**
  5. * 对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。
  6. * 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法,
  7. * 就会自动触发代理类回调)。
  8. */
  9. @Override
  10. public Object loadObject() throws Exception {
  11. System.out.println("before lazyLoader...");
  12. PropertyBean propertyBean = new PropertyBean();
  13. propertyBean.setKey("zghw");
  14. propertyBean.setValue(new TargetObject());
  15. System.out.println("after lazyLoader...");
  16. return propertyBean;
  17. }
  18. }
  19. </span>
  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import net.sf.cglib.proxy.Dispatcher;
  3. public class ConcreteClassDispatcher implements Dispatcher{
  4. @Override
  5. public Object loadObject() throws Exception {
  6. System.out.println("before Dispatcher...");
  7. PropertyBean propertyBean = new PropertyBean();
  8. propertyBean.setKey("xxx");
  9. propertyBean.setValue(new TargetObject());
  10. System.out.println("after Dispatcher...");
  11. return propertyBean;
  12. }
  13. }
  14. </span>

6.接口生成器InterfaceMaker

一、作用:

InterfaceMaker会动态生成一个接口,该接口包含指定类定义的所有方法。

二、示例:

  1. <span style="font-size:14px;">package com.zghw.cglib;
  2. import java.lang.reflect.InvocationTargetException;
  3. import java.lang.reflect.Method;
  4. import net.sf.cglib.proxy.Enhancer;
  5. import net.sf.cglib.proxy.InterfaceMaker;
  6. import net.sf.cglib.proxy.MethodInterceptor;
  7. import net.sf.cglib.proxy.MethodProxy;
  8. public class TestInterfaceMaker {
  9. public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
  10. InterfaceMaker interfaceMaker =new InterfaceMaker();
  11. //抽取某个类的方法生成接口方法
  12. interfaceMaker.add(TargetObject.class);
  13. Class<?> targetInterface=interfaceMaker.create();
  14. for(Method method : targetInterface.getMethods()){
  15. System.out.println(method.getName());
  16. }
  17. //接口代理并设置代理接口方法拦截
  18. Object object = Enhancer.create(Object.class, new Class[]{targetInterface}, new MethodInterceptor(){
  19. @Override
  20. public Object intercept(Object obj, Method method, Object[] args,
  21. MethodProxy methodProxy) throws Throwable {
  22. if(method.getName().equals("method1")){
  23. System.out.println("filter method1 ");
  24. return "mmmmmmmmm";
  25. }
  26. if(method.getName().equals("method2")){
  27. System.out.println("filter method2 ");
  28. return 1111111;
  29. }
  30. if(method.getName().equals("method3")){
  31. System.out.println("filter method3 ");
  32. return 3333;
  33. }
  34. return "default";
  35. }});
  36. Method targetMethod1=object.getClass().getMethod("method3",new Class[]{int.class});
  37. int i=(int)targetMethod1.invoke(object, new Object[]{33});
  38. Method targetMethod=object.getClass().getMethod("method1",new Class[]{String.class});
  39. System.out.println(targetMethod.invoke(object, new Object[]{"sdfs"}));
  40. }
  41. }
  42. </span>

CGLIB介绍的更多相关文章

  1. CGLIB介绍与原理

    转载: http://blog.csdn.net/zghwaicsdn/article/details/50957474 CGLIB介绍与原理(部分节选自网络) 一.什么是CGLIB? CGLIB是一 ...

  2. CGLIB介绍与原理(通过继承的动态代理)

    一.什么是CGLIB? CGLIB是一个功能强大,高性能的代码生成包.它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充.通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接 ...

  3. 思想无语言边界:以 cglib 介绍 AOP 在 java 的一个实现方式

    0. 前言 上接:基于 Source Generators 做个 AOP 静态编织小实验 作为第三篇,我们基于cglib在java做一个简单的aop例子, 以此简单作为例子说一个思路在什么样的语言里面 ...

  4. Java代理全攻略【有瑕疵:字节码生成部分没看到,最后两节没仔细看,累了】

    Java代理 1.代理模式 定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象. 其实就是委托.聚合.中间人. 为了保持行为的 ...

  5. java知识点总结

    一.java 1.容器 1)List Java中ArrayList和LinkedList区别 2)Set 理解HashSet及使用 HashMap和HashSet的区别 3Map HashMap的容量 ...

  6. 常见的 emit 实现 AOP demo

    0. 前言 上接:思想无语言边界:以 cglib 介绍 AOP 在 java 的一个实现方式 作为第四篇,我们回顾一下 csharp 里面比较常见动态编织实现方式emit 内容安排如下: emit a ...

  7. 09Spring_AOP介绍和java本身的动态代理以及cglib代理

    Aspect Oriented Programming 面向切面编程 1. 业界 AOP 实际上 OOP (面向对象编程 ) 延伸 ----  OOP编程语言. AOP设计思想,下面给出一张AOP的设 ...

  8. Spring进阶之路(10)-Advice简单介绍以及通过cglib生成AOP代理对象

    Advice简单介绍 1. Before:在目标方法运行之前运行织入.假设Before的处理中没有进行特殊的处理.那么目标方法终于会运行,可是假设想要阻止目标方法运行时.能够通过抛出一个异常来实现.B ...

  9. Spring中的cglib动态代理

    Spring中的cglib动态代理 cglib:Code Generation library, 基于ASM(java字节码操作码)的高性能代码生成包 被许多AOP框架使用 区别于JDK动态代理,cg ...

随机推荐

  1. 9.28 h5日记

    9.28 1.transparent 透明的 颜色 2.placeholder 提示语 在input中使用 跟velue不同 3.写页面需要注意的 (1)页面一定要有层次,分清层次 (2)保证元素模块 ...

  2. PHP开发——常见问题

    执行顺序 常见数据类型 字符乱码

  3. admin 的流程 Xadmin

    提交根评论 显示根评论 --- render显示 --- ajax显示 提交子评论 显示子评论 ---- render显示 ---- Ajax显示(扩展) 评论树 博客:富文本编辑器 beautful ...

  4. 团队合作之项目NABCD

    小组组长 :毛松林 组员  :张浩,谢诗语 N 我们小组要开发的项目是“高校自习室查询APP”,作为一个大学生,自学是一件很重要的能力,大学的老师不可能还像高中的老师那样整天逼着你学习,爱学不学,不学 ...

  5. L1-033 出生年(15)(STL-set代码)

    L1-033 出生年(15 分) 以上是新浪微博中一奇葩贴:"我出生于1988年,直到25岁才遇到4个数字都不相同的年份."也就是说,直到2013年才达到"4个数字都不相 ...

  6. hdu 1754(基础线段树) I Hate It

    http://acm.hdu.edu.cn/showproblem.php?pid=1754 数据比较大,暴力会超时,所以明显是线段树,普通的线段树,结构体中多开一个值sum储存每个子区间的最大成绩, ...

  7. Mercedes offline programming/coding tips and guides

    Mercedes offline programming/coding recommendations and guides: Offline coding: SCN or CVN coding wa ...

  8. linux中的设备类型

    loop设备 loop设备 一.参考命令[root@localhost a]# losetup usage:  losetup loop_device                          ...

  9. 使用Trinity拼接以及分析差异表达一个小例子

    使用Trinity拼接以及分析差异表达一个小例子  2017-06-12 09:42:47     293     0     0 Trinity 将测序数据分为许多独立的de Brujin grap ...

  10. Vue 全家桶介绍

    Vue有著名的全家桶系列,包含了vue-router(http://router.vuejs.org),vuex(http://vuex.vuejs.org), vue-resource(https: ...