CGLib浅析
CGLib浅析
什么是CGLib
CGLIB实现动态代理,并不要求被代理类必须实现接口,底层采用asm字节码生成框架生成代理类字节码(该代理类继承了被代理类)。
所以被代理类一定不能定义为final class并且对于final 方法不能被代理。
实现需要
//MethodInterceptor接口的intercept方法
/**
*obj 代理对象
*method 委托类方法,被代理对象的方法字节码对象
*arg 方法参数
*MethodProxy 代理方法MethodProxy对象,每个方法都会对应有这样一个对象
*/
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy)
Ehancer enhancer = new Enhancer() //Enhancer为字节码增强器,很方便对类进行扩展
enhancer.setSuperClass(被代理类.class);
enhancer.setCallback(实现MethodInterceptor接口的对象)
enhancer.create()
代码案例
导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
UserDaoImpl 用户实现类(RealSubject)
public class UserDaoImpl {
public boolean insert(String name) {
System.out.println("insert name=" + name);
return true;
}
public final boolean insert1(String name) {
System.out.println("final insert name=" + name);
return true;
}
}
CglibProxy CGLIB代理类(Proxy)
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("----------before----------");
System.out.println("Proxy=" + o.getClass());
System.out.println("method=" + method);
System.out.println("args=" + Arrays.toString(objects));
System.out.println("methodProxy=" + methodProxy);
//执行目标方法对象
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("----------after----------");
return result;
}
}
ProxyFactory 代理工厂
public class ProxyFactory {
private static Enhancer enhancer = new Enhancer();
private static CglibProxy cglibProxy = new CglibProxy();
public static Object getProxy(Class cls) {
enhancer.setSuperclass(cls);
enhancer.setCallback(cglibProxy);
return enhancer.create();
}
public static void main(String[] args) {
UserDaoImpl userDao = (UserDaoImpl) getProxy(UserDaoImpl.class);
userDao.insert("zc");
}
}

CGLib流程
Ehancer enhancer = new Enhancer() //Enhancer为字节码增强器,很方便对类进行扩展
enhancer.setSuperClass(被代理类.class); //为生成的类设置父类
enhancer.setCallback(实现MethodInterceptor接口的对象);
enhancer.create(); //创建代理对象
创建代理对象会经过三步:
1.生成代理类的二进制字节码文件。
2.加载二进制字节码文件到JVM,生成class对象。
3.反射获得实例构造方法,创建代理对象。
接下来,看看反编译出现的Java文件:

CGLib反编译方法
- 使用以下语句
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\xxxx")
使用
HSDB进行反编译使用
authas配合jad进行反编译
具体使用方法可以自行查找
以insert() 为入口开始:
UserDaoImpl userDao = (UserDaoImpl) getProxy(UserDaoImpl.class); //Ehancer,创建代理对象
userDao.insert("zc");
这时候会进入UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2 中的 insert()
public final boolean insert(String string) {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$insert$0$Method, new Object[]{string}, CGLIB$insert$0$Proxy);
return object == null ? false : (Boolean)object;
}
return super.insert(string);
}
其实在上述方法中,是因为设置了 enhancer.setCallback(cglibProxy); ,只要不为空,则会执行
Object object = methodInterceptor.intercept(this, CGLIB$insert$0$Method, new Object[]{string}, CGLIB$insert$0$Proxy);
this: 当前代理对象
CGLIB$say$0$Method: 目标类中的方法
CGLIB$emptyArgs: 方法参数,这里为空
CGLIB$say$0$Proxy: 代理类生成的代理方法
这样会去调用 CglibProxy.intercept() 方法
/**
* Object:cglib生成的代理对象
* Method:被代理对象方法
* Object[]:方法入参
* MethodProxy:代理的方法
*/
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("----------before----------");
//执行目标方法对象
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("----------after----------");
return result;
}
}
这时候进入 methodProxy.invokeSuper(o, objects) 方法
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
第一反应,可能不知道是f2、i2 都是什么,这里扯一下 init() 方法,其中对于FastClass 类,就是反编译出来的对应类:
private void init()
{
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1); // 被代理类FastClass
fci.f2 = helper(ci, ci.c2); // 代理类FastClass
fci.i1 = fci.f1.getIndex(sig1); // 被代理类的方法签名(index)
fci.i2 = fci.f2.getIndex(sig2); // 代理类的方法签名(index)
fastClassInfo = fci;
createInfo = null;
}
}
}
}
private static class FastClassInfo
{
FastClass f1; // 被代理类FastClass
FastClass f2; // 代理类FastClass
int i1; // 被代理类的方法签名(index)
int i2; // 代理类的方法签名(index)
}
fci.f2 = helper(ci, ci.c2); // 代理类FastClass
fci.i2 = fci.f2.getIndex(sig2); // 代理类的方法签名(index)
这时候看到UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2$$FastClassByCGLIB$$9fc87de5 中
@Override
public int getIndex(Signature signature) {
String string = ((Object)signature).toString();
switch (string.hashCode()) {
//XXXXX 省略
case -747055045: {
if (!string.equals("CGLIB$insert$0(Ljava/lang/String;)Z")) break;
return 16;
}
//XXXXX 省略
return -1;
}
所以 i2 在其中为 16 , 这时候运行下面方法
fci.f2.invoke(fci.i2, obj, args)
即,UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2$$FastClassByCGLIB$$9fc87de5 中 invoke() 方法
@Override
public Object invoke(int n, Object object, Object[] objectArray) throws InvocationTargetException {
UserDaoImpl$$EnhancerByCGLIB$.f32f6ae2 f32f6ae22 = (UserDaoImpl$$EnhancerByCGLIB$.f32f6ae2)object;
try {
switch (n) {
//XXXXX 省略
case 16: {
return new Boolean(f32f6ae22.CGLIB$insert$0((String)objectArray[0]));
}
//XXXXX 省略
}
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
可以看到,他进行调用的是 UserDaoImpl$$EnhancerByCGLIB$f32f6ae2 中的 CGLIB$insert$0() 方法
final boolean CGLIB$insert$0(String string) {
return super.insert(string);
}
这里,才是真正调用到了父类(目标类)中对应的方法。至此,整个的调用流程完毕。
流程总结
首先生成代理对象。创建增强类enhancer,设置代理类的父类,设置回调拦截方法,返回创建的代理对象;
调用代理类中的方法。这里调用的代理类中的方法实际上是重写的父类的拦截。重写的方法中会去调用
intercept方法;调用intercept,方法中会对调用代理方法中的invokeSuper方法。而在
invokeSuper中维护了一个FastClassInfo类,其包含四个属性字段,分别为FastClass f1(目标类)、FastClass f2 (代理类)、int i1(目标类要执行方法的下标)、int i2(代理类要执行方法的下标); invokeSuper中会调用的为代理类中的对应方法(代理类继承于父类的时候,对于其父类的方法,自己会生成两个方法,一个是重写的方法,一个是代理生成的方法,这里调用的即是代理生成的方法);调用代理类中的代理方法。代理方法中通过
super.xxxx(string)来实际真正的调用要执行的方法;
思考
这时候,可能心中还有一个疑惑,明明下面两个方法中都有 super.xxxx(string) , 但是使用的是 invokeSuper() ,而不是 invoke()
看下这两个方法:
final boolean CGLIB$insert$0(String string) {
return super.insert(string);
}
public final boolean insert(String string) {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$insert$0$Method, new Object[]{string}, CGLIB$insert$0$Proxy);
return object == null ? false : (Boolean)object;
}
return super.insert(string);
}
- 如果使用
invokeSuper():
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
//执行被代理类FastClass 的对应 i2 索引的方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
就是按照上面讲的步骤,先进行 insert() 方法,经过intercept,最终可以运行到 CGLIB$insert$0() ,调用到了父类(目标类)中对应的方法。
- 如果使用
invoke():
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
//执行代理类FastClass 的对应 i1 索引的方法
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (IllegalArgumentException e) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw e;
}
}
i1 的值通过下面方法获取为 1
@Override
public int getIndex(Signature signature) {
String string = ((Object)signature).toString();
switch (string.hashCode()) {
//XXXXX 省略
case -982250262: {
if (!string.equals("insert(Ljava/lang/String;)Z")) break;
return 1;
}
//XXXXX 省略
}
return -1;
}
接着,执行对应方法
@Override
public Object invoke(int n, Object object, Object[] objectArray) throws InvocationTargetException {
UserDaoImpl userDaoImpl = (UserDaoImpl)object;
try {
switch (n) {
//XXXXX 省略
case 1: {
return new Boolean(userDaoImpl.insert((String)objectArray[0]));
}
//XXXXX 省略
}
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
先进行 insert() 方法,经过intercept,通过 invoke() 方法,再次进入insert()方法,继而是一直死循环。
个人博客为:
MoYu's HomePage
CGLib浅析的更多相关文章
- Cglib动态代理浅析
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2018-06-29/18.html 作者:夜月归途 出处:http://www.guitu ...
- 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- JDK动态代理浅析
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2018-06-29/17.html 作者:夜月归途 出处:http://www.guitu ...
- 从底层源码浅析Mybatis的SqlSessionFactory初始化过程
目录 搭建源码环境 POM依赖 测试SQL Mybatis全局配置文件 UserMapper接口 UserMapper配置 User实体 Main方法 快速进入Debug跟踪 源码分析准备 源码分析 ...
- 老生常谈系列之Aop--Spring Aop原理浅析
老生常谈系列之Aop--Spring Aop原理浅析 概述 上一篇介绍了AspectJ的编译时织入(Complier Time Weaver),其实AspectJ也支持Load Time Weaver ...
- 浅析DispatchProxy动态代理AOP
浅析DispatchProxy动态代理AOP(代码源码) 最近学习了一段时间Java,了解到Java实现动态代理AOP主要分为两种方式JDK.CGLIB,我之前使用NET实现AOP切面编程,会用Fil ...
- SQL Server on Linux 理由浅析
SQL Server on Linux 理由浅析 今天的爆炸性新闻<SQL Server on Linux>基本上在各大科技媒体上刷屏了 大家看到这个新闻都觉得非常震精,而美股,今天微软开 ...
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
随机推荐
- ECShop 2.x/3.x SQL注入/任意代码执行漏洞
poc地址:https://github.com/vulhub/vulhub/blob/master/ecshop/xianzhi-2017-02-82239600/README.zh-cn.md 生 ...
- C++调试总结
一.参考: 本文主要参考<C++编程调试秘笈>一书. 在编写C++代码时,我们不应该自己捕捉缺陷,而是由编译器和可执行代码为我们做这些事情,该书便提供了这样的一个思考.作者以"调 ...
- CVPR2021 | Transformer用于End-to-End视频实例分割
论文:End-to-End Video Instance Segmentation with Transformers 获取:在CV技术指南后台回复关键字"0005"获取该论文 ...
- 第2篇-JVM虚拟机这样来调用Java主类的main()方法
在前一篇 第1篇-关于JVM运行时,开篇说的简单些 中介绍了call_static().call_virtual()等函数的作用,这些函数会调用JavaCalls::call()函数.我们看Java类 ...
- CentOS时间日期类语法
目录 一.date时间日期类 1. date显示当前时间 2. date 显示非当前时间 3. date 设置系统时间 二.cal 查看日历 一.date时间日期类 date [OPTION]... ...
- 深入理解jvm-2Edition-类文件结构
概述: 规范而独立的类文件结构以及只与类文件关联的虚拟机为Java实现了平台无关性,甚至还带来了一些语言无关性. 只要将源代码编译为Class文件规定的格式,JVM就可以执行. JVM的指令描述能力比 ...
- 记客户端出现Connect reset问题排查。
客户访问我们地址出现Connect reset. 网上查询说是服务端关闭,客户端还在读,就会出现Connect reset. 我们就排查为什么服务端会关闭. 网络的同事说收到了客户端的信息,但是被服务 ...
- DVWA(九):File Upload 全等级文件上传
File Upload 文件上传,通常是由于对上传文件的类型没有进行严格的过滤.限制造成的,一般思路是 通过上传木马获取服务器的webshell(通过网络端口对网站服务器某种程度上的操作权限 也叫网站 ...
- Linux部署PgSQL数据库
1.安装: yum install postgresql-server -y postgresql-setup initdb cd /var/lib/pgsql/data 2.进入 /var/lib/ ...
- NOIP 模拟 $22\; \rm e$
题解 对于这个 \(abs\) 就是求大于 \(r\) 的最小值,小于 \(r\) 的最大值,建权值线段树或平衡树. 因为是 \(k\) 个点的联通块,就是求它们的 \(lca\) 到它们的链,可持久 ...