什么是cglib

CGLIB(Code Generator Library)是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。

为什么使用CGLIB

CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。

CGLIB组织结构

CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。ASM的使用可以参考

使用实例

CGLIB创建代理类

maven 引入cglib包。

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>

创建被代理类

public class OtherClass {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public void test(){System.out.println("Other Class。。。");}
}

使用cglib创建代理

    Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OtherClass.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("调用之前");// 注意这里调用的是invokeSuper,还有一个方法invoke调用的代理本身的方法,会造成Stack OverflowObject result = methodProxy.invokeSuper(o, objects);System.out.println("调用之后");return result;}});OtherClass otherClass = (OtherClass) enhancer.create();System.out.println("代理类是:" + otherClass.getClass().getSimpleName());System.out.println("代理的父类是:" + otherClass.getClass().getSuperclass().getSimpleName());otherClass.test();

console 输出:

代理类是:OtherClass$$EnhancerByCGLIB$$ebef5aab
代理的父类是:OtherClass
调用之前
Other Class。。。
调用之后

固定返回值代理

代理目标类的方法放回固定值,会代理目标类所有非final方法,返回固定的值。

public class OtherClass {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public void test(){System.out.println("Other Class。。。");}
}Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OtherClass.class);enhancer.setCallback(new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "fixed value";}});OtherClass otherClass = (OtherClass) enhancer.create();// 方法无返回值,有被fixedValue代理了,无效otherClass.test();// 代理getName返回固定值System.out.println(otherClass.getName());// getClass是final的不代理System.out.println(otherClass.getClass().getSimpleName());// 代理了hashCode但hashCode返回是int,代理返回的是str,抛出异常System.out.println(otherClass.hashCode());

console 输出:

fixed value
OtherClass$$EnhancerByCGLIB$$7ecc18d3
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

CGLIB的InvocationHandler

看到InvocationHandler就有中亲切的感觉,用法和jdk的InvocationHandler差不多。注意到的包是cglib的

    final OtherClass o = new OtherClass();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OtherClass.class);enhancer.setCallback(new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){return "invocation Handler";}else{return method.invoke(o,args);}}});OtherClass otherClass = (OtherClass) enhancer.create();System.out.println(otherClass.getName());System.out.println(otherClass.getClass().getSimpleName());System.out.println(otherClass.hashCode());

console 输出:

invocation Handler
OtherClass$$EnhancerByCGLIB$$7ecc18d3
598446861

CallbackHelper

对特定的方法进行拦截

    Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OtherClass.class);CallbackHelper callbackHelper = new CallbackHelper(OtherClass.class, new Class[0]) {@Overrideprotected Object getCallback(Method method) {// 过滤代理方法if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){return new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib";}};}else{// Methods using this Enhancer callback will delegate directly to the default (super) implementation in the base class.// 最直接调用被代理类return NoOp.INSTANCE;}}};enhancer.setCallbackFilter(callbackHelper);enhancer.setCallbacks(callbackHelper.getCallbacks());OtherClass otherClass = (OtherClass) enhancer.create();System.out.println(otherClass.getName());System.out.println(otherClass.getClass().getSimpleName());System.out.println(otherClass.hashCode());

console 输出:

Hello cglib
OtherClass$$EnhancerByCGLIB$$dd97cb24
2117255219

不可变对象

ImmutableBean允许创建一个原来对象的包装类,这个包装类是不可变的。不可以通过包装类来修改对象。

    OtherClass otherClass = new OtherClass();otherClass.setName("hahaha");OtherClass otherClass2 = (OtherClass) ImmutableBean.create(otherClass);System.out.println(otherClass2.getName());otherClass.setName("hehe");System.out.println(otherClass2.getName());otherClass2.setName("throw exception");

console 输出:

hahaha
hehe
Exception in thread "main" java.lang.IllegalStateException: Bean is immutable

动态创建bean

BeanGenerator 相当于一个小工具类,可以生成简单的bean

    BeanGenerator beanGenerator = new BeanGenerator();beanGenerator.addProperty("value",String.class);Object myBean = beanGenerator.create();// 自动生成setter,getter方法。Method setter = myBean.getClass().getMethod("setValue",String.class);setter.invoke(myBean,"Hello cglib");Method getter = myBean.getClass().getMethod("getValue");System.out.println(getter.invoke(myBean));

属性复制 Bean Copier

把两个类进行属性复制。属性名要一致。

    // 设置是否使用converterBeanCopier copier = BeanCopier.create(OtherClass.class, OtherClass2.class, false);OtherClass bean1 = new OtherClass();bean1.setName("Hello cglib");OtherClass2 otherBean = new OtherClass2();// 如果指定不使用转换器传null就好// 将bean1中的相同属性赋值到其他bean中copier.copy(bean1, otherBean, null);System.out.println(bean1.getName());System.out.println(otherBean.getName());

将bean1中的属性复制到了 otherBean中。

console 输出:

Hello cglib
Hello cglib

BulkBean 属性获取设置

public class OtherClass {private String name;private String address;... 省略setter,getter
}BulkBean bulkBean = BulkBean.create(OtherClass.class,new String[]{"getName","getAddress"},new String[]{"setName","setAddress"},new Class[]{String.class,String.class});OtherClass bean = new OtherClass();bean.setName("Hello world");Object[] propertyValues = bulkBean.getPropertyValues(bean);// 获取第一个属性值System.out.println(propertyValues[0]);// 设置属性值bulkBean.setPropertyValues(bean,new Object[]{"Hello cglib","address"});System.out.println(bean.getName());System.out.println(bean.getAddress());

console 输出:

Hello world
Hello cglib
address

BeanMap

将对象的属性转换位一个map<属性名,属性值>

// beanMap 是map的一直子类,直接通过set,get读取
BeanMap map = BeanMap.create(bean);

接口创建对象

keyFactory类用来动态生成接口的实例,接口需要只包含一个newInstance方法,返回一个Object。keyFactory为构造出来的实例动态生成了Object.equals和Object.hashCode方法,能够确保相同的参数构造出的实例为单例的。

interface SampleKeyFactory {Object newInstance(String first, int second);
}SampleKeyFactory keyFactory = (SampleKeyFactory) KeyFactory.create(SampleKeyFactory.class);Object key = keyFactory.newInstance("foo", 42);Object key1 = keyFactory.newInstance("foo", 42);System.out.println(key instanceof SampleKeyFactory);System.out.println(key .equals( key1));System.out.println(key.hashCode());System.out.println(key1.hashCode());

console 输出:

true
true
-1181409833
-1181409833

动态生成接口

    // 方法名;返回值;接受参数;Signature signature = new Signature("foo", Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE});Signature signature2 = new Signature("foo2", Type.getType(String.class),new Type[]{Type.INT_TYPE});InterfaceMaker interfaceMaker = new InterfaceMaker();// 添加方法;声明异常信息interfaceMaker.add(signature, new Type[]{Type.getType(Exception.class)});interfaceMaker.add(signature2, new Type[0]);Class iface = interfaceMaker.create();System.out.println(iface.getMethods().length);System.out.println(iface.getMethod("foo",int.class).getReturnType());System.out.println(iface.getMethod("foo",int.class).getExceptionTypes()[0]);System.out.println(iface.getMethod("foo2",int.class).getReturnType());

console 输出:

2
double
class java.lang.Exception
class java.lang.String

方法代理

接口要创建在一个单独的文件中

关于Method.create的参数说明:

  1. 第二个参数为即将被代理的方法
  2. 第一个参数必须是一个无参数构造的bean。因此MethodDelegate.create并不是你想象的那么有用
  3. 第三个参数为只含有一个方法的接口。当这个接口中的方法被调用的时候,将会调用第一个参数所指向bean的第二个参数方法

缺点:

  1. 为每一个代理类创建了一个新的类,这样可能会占用大量的永久代堆内存
  2. 你不能代理需要参数的方法
  3. 如果你定义的接口中的方法需要参数,那么代理将不会工作,并且也不会抛出异常;如果你的接口中方法需要其他的返回类型,那么将抛出IllegalArgumentException
// 单独文件中创建接口
public interface BeanDelegate {String getNameFromDelegate();
}OtherClass bean = new OtherClass();bean.setName("Hello cglib");BeanDelegate delegate = (BeanDelegate) MethodDelegate.create(bean,"getName", BeanDelegate.class);// 调用接口将会去调用指定了的指定方法System.out.println(delegate.getNameFromDelegate());

console 输出:

Hello cglib

多重方法代理

只用接口代理多个实现类的方法。

  1. 多重代理和方法代理差不多,都是将代理类方法的调用委托给被代理类。使用前提是需要一个接口,以及一个类实现了该接口
  2. 通过这种interface的继承关系,我们能够将接口上方法的调用分散给各个实现类上面去。
  3. 多重代理的缺点是接口只能含有一个方法,如果被代理的方法拥有返回值,那么调用代理类的返回值为最后一个添加的被代理类的方法返回值
public interface DelegatationProvider {void setValue(String value);
}
public class SimpleMulticastBean implements DelegatationProvider {private String value;@Overridepublic void setValue(String value) {this.value = value;}public String getValue() {return value;}
}MulticastDelegate multicastDelegate = MulticastDelegate.create(DelegatationProvider.class);SimpleMulticastBean first = new SimpleMulticastBean();SimpleMulticastBean second = new SimpleMulticastBean();multicastDelegate = multicastDelegate.add(first);multicastDelegate  = multicastDelegate.add(second);DelegatationProvider provider = (DelegatationProvider) multicastDelegate;provider.setValue("Hello world");System.out.println(first.getValue());System.out.println(second.getValue());

console 输出:

Hello world
Hello world

构造器代理

为了对构造函数进行代理,我们需要一个接口,这个接口只含有一个Object newInstance(…)方法,用来调用相应的构造函数

public interface SampleBeanConstructorDelegate {Object newInstance(String value);
}
public class OtherClass {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public OtherClass(String name) {this.name = name;}
}SampleBeanConstructorDelegate constructorDelegate = (SampleBeanConstructorDelegate) ConstructorDelegate.create(OtherClass.class, SampleBeanConstructorDelegate.class);OtherClass bean = (OtherClass) constructorDelegate.newInstance("Hello world");System.out.println(bean.getName());

console 输出:

Hello world

FastClass

顾明思义,FastClass就是对Class对象进行特定的处理,比如通过数组保存method引用,因此FastClass引出了一个index下标的新概念,比如getIndex(String name, Class[] parameterTypes)就是以前的获取method的方法。通过数组存储method,constructor等class信息,从而将原先的反射调用,转化为class.index的直接调用,从而体现所谓的FastClass。

    FastClass fastClass = FastClass.create(OtherClass.class);FastMethod fastMethod = fastClass.getMethod("getName",new Class[0]);OtherClass bean = new OtherClass();bean.setName("Hello world");Object result = fastMethod.invoke(bean, new Object[0]);System.out.println(result.toString());

console 输出:

Hello world

参考链接

咸鱼IT技术交流群:89248062,在这里有一群和你一样有爱、有追求、会生活的朋友! 大家在一起互相支持,共同陪伴,让自己每天都活在丰盛和喜乐中!同时还有庞大的小伙伴团体,在你遇到困扰时给予你及时的帮助,让你从自己的坑洞中快速爬出来,元气满满地重新投入到生活中!

Java成神之路——CGLIB使用相关推荐

  1. Java成神之路技术整理

    转载自 Java成神之路技术整理 以下是Java技术栈微信公众号发布的所有关于 Java 的技术干货,会从以下几个方面汇总,本文会长期更新. Java 基础篇 Java 集合篇 Java 多线程篇 J ...

  2. Java成神之路[转]

    阿里大牛珍藏架构资料,点击链接免费获取 针对本文,博主最近在写<成神之路系列文章> ,分章分节介绍所有知识点.欢迎关注. 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 ...

  3. Alibaba技术专家倾心五年打造 Java成神之路:基础篇

    近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是Java的基础知识?又怎么样才算掌握了java的基础知识呢?这个问题还真值得仔细思考. ...

  4. Java成神之路——ASM,Javassist,cglib区别。

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  5. 【学海无涯】Java成神之路

    基础篇 面向对象 面向对象与面向过程   面向过程就是按照程序进行的顺序依次编写索要完成相应任务的方法,依次调用.面型对象注重对逻辑概念的封装,将若干变量和方法封装成类,各个对象互相调用.面向对象占用 ...

  6. Java成神之路——javaAgent(插桩,attach)

    javaAgent Javaagent 是什么? javaAgent运行类加载器在加载类之前对类做出动态的修改. 运行java命令执行时添加参数 -javaagent指定打包好的agent的jar即可 ...

  7. Java成神之路技术整理,本文长期更新!

    原文地址:https://mp.weixin.qq.com/s/N507Cfb_mbkGvHtg_FIaVg(来源:java技术栈微信公众号) 以下是Java技术栈微信公众号发布的所有关于 Java ...

  8. 夜光:Java成神之路(四)擅长的语言

    夜光序言: 女孩问:"你喜欢我吗?" "不喜欢"男孩不假思索的回答 "哦"女孩失落地低下头,眼泪快要掉下来 "傻瓜,我不喜欢你,我 ...

  9. Java成神之路——JVM垃圾回收概览

    如何确认对象可以被回收 枚举根节点,来确认, 搜索对象的引用链. 当一个对象的引用不能到达根节点,那么就认为这个对象是垃圾. 根节点可以为: 虚拟机栈中引用的对象,方法区中类静态属性引用的遍历,方法区 ...

最新文章

  1. stm32f103zet6linux,stm32f103zet6定时器详解及应用
  2. [NSTaggedPointerString text]:unrecognized selector sent to instance
  3. HDU-4568 Hunter 状态压缩
  4. 什么端口可以抓LINUX,linux下抓包工具
  5. C#判断Textbox是否为数字
  6. gson 转换 ListMap 注意事项
  7. JVM005_synchronized、同步指令、管程、MESA
  8. 深入探索PowerPivot客户端和服务器端架构
  9. 还是畅通工程 最小生成树
  10. 中国水下充气袋行业市场供需与战略研究报告
  11. knx智能照明控制系统电路图_智能照明控制系统(KNX)教材
  12. u深度重装系统详细教程_U盘怎样使用U深度给电脑装系统教程
  13. Pycharm+Django之Django学习(1)(初学者)
  14. 山东 计算机专业,山东省内计算机专业大学排名?
  15. 解析几何:第一章坐标系与坐标变换:平面直角坐标系、空间直角坐标系及其变换、极坐标系,圆柱面坐标系,球面坐标系
  16. 趋势科技发布《2007年病毒威胁报告暨2008年预测》
  17. 音频转文字怎么操作?快来看看这几个方法吧
  18. 电压压力蕊片_杀鸡取卵~拆SFAIF工业压力仪表取主控芯片ICL7126修液晶电压表头~更多高级仪表果照...
  19. 不花一分钱,利用免费电脑软件将视频MV变成歌曲音频MP3
  20. 陕西的大学计算机排名2015年,陕西省大学排名前15名,西安交大第一,985的西北农大竟无缘前三...

热门文章

  1. windows 10 删除Linux系统方法
  2. iPhone 14 Pro系列传出好消息,有望实现8GB内存自由
  3. 阿斯顿马丁宣布放弃内燃机:2026年开始只销售电动或混动车
  4. 一加10 Pro首发定制X轴线性马达:Top级超大体积 安卓阵营马达天花板
  5. 领英“顶尖公司”榜单出炉:华为、字节跳动位居前二
  6. 北京文化:目前《你好李焕英》贡献营收约6000万至6500万元
  7. 微信支付分使用用户数超2.4亿 每日使用笔数达千万级
  8. 小米集团公布新任CFO人选:系原瑞信亚太区高管
  9. 阿里巴巴公布香港IPO定价:每股176港元
  10. 淘宝可以改名字了?并没有!当初的年少轻狂,现在的不忍直视...