原文 https://blog.csdn.net/makecontral/article/details/79593732

Cglib的实例

本文重在源码的分析,Cglib的使用不再复述。

//被代理类
public class InfoDemo {
public void welcome (String person){
System.out.println("welcome :" + person);
}
} public class CglibInfoProxy implements MethodInterceptor {
private Object target;
public Object newInstance(Object source){
target = source;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
} @Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before method!!!");
Object value = methodProxy.invokeSuper(o, objects);
//Object value = methodProxy.invoke(o, objects);
return value;
}
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
instance.welcome("zhangsan");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
代理类字节码分析 先来看main函数中new CglibInfoProxy().newInstance(new InfoDemo()),简单来说这个方法是将infoDemo作为一个父类,通过asm字节码生成一个子类来继承infoDemo。
InfoDemo$$EnhancerByCGLIB$$8b8da05b.class,就是生成的子类的字节码,称这个子类为InfoDemo的代理类。截取部分字节码显示: public class InfoDemo$$EnhancerByCGLIB$$8b8da05b extends InfoDemo implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$welcome$0$Method;
private static final MethodProxy CGLIB$welcome$0$Proxy;
//..........................................省略
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("CglibTest.InfoDemo$$EnhancerByCGLIB$$8b8da05b");
Class var1;
CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("CglibTest.InfoDemo")).getDeclaredMethods())[0];
CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
//..........................................省略
} final void CGLIB$welcome$0(String var1) {
super.welcome(var1);
}
//先来判断这个代理类中是否设置了方法拦截。如果设置了就调用该拦截器的intercept方法。
//在本程序中,我们是设置了拦截器的。enhancer.setCallback(this);
public final void welcome(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if(var10000 != null) {
var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
} else {
super.welcome(var1);
}
}
//..........................................省略
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
在这个代理类中,这里有两点需要注意。
1.它会将父类中的每一个方法,生成两个与之对应。如父类中的welcome,在代理类中就会有CGLIBwelcomewelcome0,welcome的两个方法与之对应。
2.每一个方法都会静态块中,经过MethodProxy.create生成对应的方法代理。如
MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0"); 当main函数执行到instance.welcome(“zhangsan”);这个语句时,会进入到代理类中的public final void welcome(String var1) 方法。进而执行了var10000.intercept()方法。
根据CglibInfoProxy中的intercept,先是会输出一句“before method!!!”,然后调用methodProxy.invokeSuper(o, objects);
这里的methodProxy对应的是var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy)中的CGLIB$welcome$0$Proxy。 那么就相当于执行CGLIB$welcome$0$Proxy.invokeSuper(o, objects),那么来看看invokeSuper在做什么。 MethodProxy 源码分析 public class MethodProxy {
//下面的前三个变量在create方法中,都已经得到了初始值了。
private Signature sig1;
private Signature sig2;
private MethodProxy.CreateInfo createInfo;
//FastClassInfo是在调用methodProxy.invoke或者methodProxy.invokeSuper中,init()会触发,后面再来细看这个。
private volatile MethodProxy.FastClassInfo fastClassInfo; public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if(this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
//本质就是要生成一个fastClassInfo,fastClassInfo里面存放着两个fastclass,f1,f2。
//还有两个方法索引的值i1,i2。
private void init() {
if(this.fastClassInfo == null) {
Object var1 = this.initLock;
synchronized(this.initLock) {
if(this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
//难道每一个方法,我们都去生成一个fastclass吗?
//不是的,每一个方法的fastclass都是一样的,只不过他们的i1,i2不一样。如果缓存中就取出,没有就生成新的FastClass
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
//根据一个类的信息,返回的该对象的一个Fastclass
private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
Generator g = new Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
init()的过程就是生成FastClassInfo。
对于FastClass可以看看这篇文章https://www.cnblogs.com/cruze/p/3865180.html fci.f2.invoke(fci.i2, obj, args);fci存放了两个类的fastClass。
其中f1是被代理的类对应的是InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class,
f2InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c.class
这也就是为什么,在classes文件中会生成三个class文件了,一个代理类,两个fastclass.
f2.invoke,那就说明会调用InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c这个类的方法。 代理类的FastClass 那再看看代理类的fastClass的字节码长啥样
贴出InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c的部分字节码: public class InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c extends FastClass { public InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -2055565910:
if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 12;
}
break;
case -1725733088:
if(var10000.equals("getClass()Ljava/lang/Class;")) {
return 24;
}
case 1013143764:
if(var10000.equals("CGLIB$welcome$0(Ljava/lang/String;)V")) {
return 17;
}
}
//----省略
} public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
8b8da05b var10000 = (8b8da05b)var2;
int var10001 = var1; try {
switch(var10001) {
case 0:
return var10000.toString();
case 1:
return new Integer(var10000.hashCode()); case 17:
var10000.CGLIB$welcome$0((String)var3[0]);
}
//----省略
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
可以看到,会执行到voke方法中的
var10000.CGLIB$welcome$0((String)var3[0]);,而CGLIB$welcome$0其实就是直接调用了super.welcome(var1)的方法。输出结束之后就会运行完毕。 那么,如果现在调用的是methodProxy.invoke(o, objects);而不是invokeSuper会是怎么样的情况呢?
通过上面MethodProxy的源码,可以看到当执行invoke的时候会执行到fci.f1.invoke(fci.i1, obj, args);
即会调用被代理类的fastclass,InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class中的invoke。
打开该class文件我们会发现,执行的的是welcome()。 public Object invoke(Object obj, Object[] args) throws Throwable {
try {
switch(var10001) {
case 0:
var10000.welcome((String)var3[0]);
return null;
case 1:
}
}
1
2
3
4
5
6
7
8
9
那不就是和在main函数中instance.welcome(“zhangsan”)一样的步骤了嘛,就会又开始循环调用,一直到栈溢出报错。所以,invoke会造成OOM的问题。

  

Cglib源码分析 invoke和invokeSuper的差别(转)的更多相关文章

  1. cglib源码分析(一): 缓存和KEY

    cglib是一个java 字节码的生成工具,它是对asm的进一步封装,提供了一系列class generator.研究cglib主要是因为它也提供了动态代理功能,这点和jdk的动态代理类似. 一. C ...

  2. cglib源码分析--转

    原文地址:http://www.iteye.com/topic/799827 背景 前段时间在工作中,包括一些代码阅读过程中,spring aop经常性的会看到cglib中的相关内容,包括BeanCo ...

  3. cglib源码分析(四):cglib 动态代理原理分析

    本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ...

  4. cglib源码分析(二):Class name 生成策略

    一.如何获取动态生成的class 字节码 结合生成的class文件是一个学习cglib的比较好的方法.在cglib中,生成的class文件默认只存储在内存中,我们可以在代码中加入下面语句来获取clas ...

  5. cglib源码分析(三):Class生成策略

    cglib中生成类的工作是由AbstractClassGenerator的create方法使用相应的生成策略完成,具体代码如下: private GeneratorStrategy strategy ...

  6. spring源码分析之spring-core总结篇

    1.spring-core概览 spring-core是spring框架的基石,它为spring框架提供了基础的支持. spring-core从源码上看,分为6个package,分别是asm,cgli ...

  7. Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理

    AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...

  8. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  9. spring源码分析(二)Aop

    创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...

随机推荐

  1. js创建对象的三种方法

    1.使用对象初始化器:{} var person = {....} 2 var person=new object() function person(参数) { this.参数=... } var ...

  2. 洛谷P3225 HNOI2012 矿场搭建

    题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之 ...

  3. html5(三)

    body{ text-align:center;} #mainbox { display:block; width:500px; margin:50px auto; padding:15px; tex ...

  4. noip模拟ernd

    [题目背景]解决了第一题之后,你打开了第二题.这是一道关于树的题目,你很快想出了一个复杂度和树的直径有关的算法,可以通过所有的数据.不过,你的做法常数似乎有点大.为了更好地卡常,你决定构造一些数据来检 ...

  5. Windows下dos命令行

    Dos命令--基本命令 1.求助命令 例如:del /? rd /? 2.基本常用命令 例如:dir :列出当前目录下的文件以及文件夹,后面可以接其他路径 md:创建目录(mkdir),一次创建多级目 ...

  6. JavaScript实现本地图片上传前进行裁剪预览

    本项目支持IE8+,测试环境IE8,IE9,IE10,IE11,Chrome,FireFox测试通过 另:本项目并不支持Vue,React等,也不建议,引入JQuery和Vue.React本身提倡的开 ...

  7. python自学第11天-单线程并发、迭代器,序列化,获取路径

    单线程并发 import time def consumer(name): print("%s 准备吃包子了"%name) while True: baozi=yield#变成一个 ...

  8. sql循环查询树形结构

    pid:父类别id ' connect by prior pid = id --查询父祖类别 union ' connect by prior id = pid;--查询子孙类别 这样查询出选中id ...

  9. 剑指Offer 45. 扑克牌顺子 (其他)

    题目描述 LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决 ...

  10. Using Dispatcher

    he following code example shows how you might attempt to update the UI from your task logic. // The ...