Cglib及其基本使用
前言
最近一直在看Spring源码,其实我之前一直知道AOP的基本实现原理:
- 如果针对接口做代理默认使用的是JDK自带的Proxy+InvocationHandler
- 如果针对类做代理使用的是Cglib
- 即使针对接口做代理,也可以将代理方式配置成走Cglib的
之后要看AOP源码了,Proxy+InvocationHandler这套我已经很熟了,想着如果把Cglib研究研究,应该看AOP源码的时候会更快一些,因此本文就研究一下Cglib是什么、如何一些基本的使用、底层实现原理。
Cglib是什么
Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。下图是我网上找到的一张Cglib与一些框架和语言的关系:

对此图总结一下:
- 最底层的是字节码Bytecode,字节码是Java为了保证“一次编译、到处运行”而产生的一种虚拟指令格式,例如iload_0、iconst_1、if_icmpne、dup等
- 位于字节码之上的是ASM,这是一种直接操作字节码的框架,应用ASM需要对Java字节码、Class结构比较熟悉
- 位于ASM之上的是CGLIB、Groovy、BeanShell,后两种并不是Java体系中的内容而是脚本语言,它们通过ASM框架生成字节码变相执行Java代码,这说明在JVM中执行程序并不一定非要写Java代码----只要你能生成Java字节码,JVM并不关心字节码的来源,当然通过Java代码生成的JVM字节码是通过编译器直接生成的,算是最“正统”的JVM字节码
- 位于CGLIB、Groovy、BeanShell之上的就是Hibernate、Spring AOP这些框架了,这一层大家都比较熟悉
- 最上层的是Applications,即具体应用,一般都是一个Web项目或者本地跑一个程序
使用Cglib代码对类做代理
下面演示一下Cglib代码示例----对类做代理。首先定义一个Dao类,里面有一个select()方法和一个update()方法:
public class Dao {
public void update() {
System.out.println("PeopleDao.update()");
}
public void select() {
System.out.println("PeopleDao.select()");
}
}
创建一个Dao代理,实现MethodInterceptor接口,目标是在update()方法与select()方法调用前后输出两句话:
public class DaoProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("Before Method Invoke");
proxy.invokeSuper(object, objects);
System.out.println("After Method Invoke");
return object;
}
}
intercept方法的参数名并不是原生的参数名,我做了自己的调整,几个参数的含义为:
- Object表示要进行增强的对象
- Method表示拦截的方法
- Object[]数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
- MethodProxy表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
写一个测试类:
public class CglibTest {
@Test
public void testCglib() {
DaoProxy daoProxy = new DaoProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallback(daoProxy);
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
}
这是使用Cglib的通用写法,setSuperclass表示设置要代理的类,setCallback表示设置回调即MethodInterceptor的实现类,使用create()方法生成一个代理对象,注意要强转一下,因为返回的是Object。最后看一下运行结果:
Before Method Invoke
PeopleDao.update()
After Method Invoke
Before Method Invoke
PeopleDao.select()
After Method Invoke
符合我们的期望。
使用Cglib定义不同的拦截策略
再扩展一点点,比方说在AOP中我们经常碰到的一种复杂场景是:我们想对类A的B方法使用一种拦截策略、类A的C方法使用另外一种拦截策略。
在本例中,即我们想对select()方法与update()方法使用不同的拦截策略,那么我们先定义一个新的Proxy:
public class DaoAnotherProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("StartTime=[" + System.currentTimeMillis() + "]");
method.invoke(object, objects);
System.out.println("EndTime=[" + System.currentTimeMillis() + "]");
return object;
}
}
方法调用前后输出一下开始时间与结束时间。为了实现我们的需求,实现一下CallbackFilter:
public class DaoFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if ("select".equals(method.getName())) {
return 0;
}
return 1;
}
}
返回的数值表示顺序,结合下面的代码解释,测试代码要修改一下:
public class CglibTest {
@Test
public void testCglib() {
DaoProxy daoProxy = new DaoProxy();
DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallbacks(new Callback[]{daoProxy, daoAnotherProxy, NoOp.INSTANCE});
enhancer.setCallbackFilter(new DaoFilter());
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
}
意思是CallbackFilter的accept方法返回的数值表示的是顺序,顺序和setCallbacks里面Proxy的顺序是一致的。再解释清楚一点,Callback数组中有三个callback,那么:
- 方法名为"select"的方法返回的顺序为0,即使用Callback数组中的0位callback,即DaoProxy
- 方法名不为"select"的方法返回的顺序为1,即使用Callback数组中的1位callback,即DaoAnotherProxy
因此,方法的执行结果为:
StartTime=[1491198489261]
PeopleDao.update()
EndTime=[1491198489275]
Before Method Invoke
PeopleDao.select()
After Method Invoke
符合我们的预期,因为update()方法不是方法名为"select"的方法,因此返回1,返回1使用DaoAnotherProxy,即打印时间;select()方法是方法名为"select"的方法,因此返回0,返回0使用DaoProxy,即方法调用前后输出两句话。
这里要额外提一下,Callback数组中我特意定义了一个NoOp.INSTANCE,这表示一个空Callback,即如果不想对某个方法进行拦截,可以在DaoFilter中返回2,具体效果可以自己尝试一下。
构造函数不拦截方法
如果Update()方法与select()方法在构造函数中被调用,那么也是会对这两个方法进行相应的拦截的,现在我想要的是构造函数中调用的方法不会被拦截,那么应该如何做?先改一下Dao代码,加一个构造方法Dao(),调用一下update()方法:
public class Dao {
public Dao() {
update();
}
public void update() {
System.out.println("PeopleDao.update()");
}
public void select() {
System.out.println("PeopleDao.select()");
}
}
如果想要在构造函数中调用update()方法时,不拦截的话,Enhancer中有一个setInterceptDuringConstruction(boolean interceptDuringConstruction)方法设置为false即可,默认为true,即构造函数中调用方法也是会拦截的。那么测试方法这么写:
public class CglibTest {
@Test
public void testCglib() {
DaoProxy daoProxy = new DaoProxy();
DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallbacks(new Callback[]{daoProxy, daoAnotherProxy, NoOp.INSTANCE});
enhancer.setCallbackFilter(new DaoFilter());
enhancer.setInterceptDuringConstruction(false);
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
}
运行结果为:
PeopleDao.update()
StartTime=[1491202022297]
PeopleDao.update()
EndTime=[1491202022311]
Before Method Invoke
PeopleDao.select()
After Method Invoke
看到第一次update()方法的调用,即Dao类构造方法中的调用没有拦截,符合预期。
后记
本文演示了一些Cglib的基本用法,由于Cglib的原理探究篇幅比较长,就不放在本文写了,会在下一篇文章中写。
想要深入使用Cglib的朋友还需要多多尝试Cglib的各种API,才能更好地使用这个优秀的字节码生成框架。
Cglib及其基本使用的更多相关文章
- 基于Spring AOP的JDK动态代理和CGLIB代理
一.AOP的概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...
- cglib动态新增类方法
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> & ...
- cglib动态代理
代理即为访问对象添加一层控制层,使其间接化,控制层可以为对象访问添加操作属性. cglib:Code Generation library, 基于ASM(java字节码操作码)的高性能代码生成包 被许 ...
- CGLib动态代理原理及实现
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了.CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采 ...
- java反射 cglib asm相关资料
有篇文章对java反射的调用的效率做了测试,写的比较好.猛击下面地址 http://www.blogjava.net/stone2083/archive/2010/09/15/332065.html ...
- Spring中的cglib动态代理
Spring中的cglib动态代理 cglib:Code Generation library, 基于ASM(java字节码操作码)的高性能代码生成包 被许多AOP框架使用 区别于JDK动态代理,cg ...
- 【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】
一.cglib动态代理 1.简介 (1)CGlib是一个强大的,高性能,高质量的Code生成类库.它可以在运行期扩展Java类与实现Java接口. (2) 用CGlib生成代理类是目标类的子类. (3 ...
- AOP学习心得&jdk动态代理与cglib比较
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...
- Java 的静态代理 动态代理(JDK和cglib)
转载:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是 ...
- Java动态代理与Cglib库
JDK动态代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在 ...
随机推荐
- oracle 11.2.0.2以后对数据库用户名重命名
本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn [转自]http://www.xifenfei.com/2012/0 ...
- (一)《Maven实战》读书笔记 —— Maven简介
第一章:Maven简介 一.何为Maven? Maven这个词可以翻译为"知识的积累",本书将介绍Maven这一跨平台的项目管理工具.作为Apache组织中的一个个颇为成功的开源项 ...
- YUM安装软件
YUM:介绍.工作流程.本地yum.网络yum.yum的相关命令 一.What is YUM YUM是基于rpm但更胜于rpm的软件管理工具 YUM的优点: 1.更方便的管理rpm软件包 2.自动解决 ...
- Hibernate自动生成实体类注解(转)
常用的hibernate annotation标签如下: @Entity --注释声明该类为持久类.将一个Javabean类声明为一 个实体的数据库表映射类,最好实现序列化.此时,默认情况下,所有的类 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少分配率, 最重要的规则,缩短对象的生命周期,减少对象层次的深度,减少对象之间的引用,避免钉住对象(Pinning)
减少分配率 这个几乎不用解释,减少了内存的使用量,自然就减少GC回收时的压力,同时降低了内存碎片与CPU的使用量.你可以用一些方法来达到这一目的,但它可能会与其它设计相冲突. 你需要在设计对象时仔细检 ...
- crack the coding interview
crack the coding interview answer c++ 1.1 #ifndef __Question_1_1_h__ #define __Question_1_1_h__ #i ...
- 通过 Service 访问 Pod - 每天5分钟玩转 Docker 容器技术(136)
本节开始学习 Service.我们不应该期望 Kubernetes Pod 是健壮的,而是要假设 Pod 中的容器很可能因为各种原因发生故障而死掉.Deployment 等 controller 会通 ...
- Shell脚本的基本流程控制
if else read -p '请输入分数:' score if [ $score -lt 60 ]; then echo '60分以下' elif [ $score -lt 70 ]; then ...
- ACdream 1015 Double Kings 树的重心
思路:删除根结点,其最大子树的节点最少.求一次树的重心即可. AC代码 #include <cstdio> #include <cmath> #include <ccty ...
- redis下载安装以及添加服务
一.下载安装 从 github 下载:https://github.com/MSOpenTech/redis/releases 或者 https://github.com/ServiceStack/r ...