原文 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. ubuntu文档保存出现的一些错误

    ubuntu使用vim编辑器保存时,出现了错误,虽然知道基本的保存方法,但是还不够,出现各种错误.基本的保存命令: 写入文件后退出保存:wq后,保存时出现错误E45:已设定选项“readonly”(请 ...

  2. DevExpress.Mvvm.Free

    DevExpress MVVM Framework is a set of components helping to work in the Model-View-ViewModel pattern ...

  3. os、os.path模块(文件/目录方法)

    1.模块的概念:模块是一个包含所有定义的变量.函数的文件,模块可以被其余模块调用. 2.利用OS模块实现对系统文件的. os模块中常见的方法: gercwd()     返回当前工作目录 chdir( ...

  4. NOIP2012提高组 Day 2 Problem 2 借教室

    原题 题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我 ...

  5. IDE Fix Pack 6.4.2 released (bugfix release)

    IDE Fix Pack 6.4.2 addresses two bugs. It fixes an issue with the TCustomListBox.ResetContent patch ...

  6. 剑指Offer 40. 数组中只出现一次的数字 (数组)

    题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了偶数次.请写程序找出这两个只出现一次的数字. 题目地址 https://www.nowcoder.com/practice/e02fdb54 ...

  7. Windows文件夹、文件源代码对比工具--WinMerge

    /********************************************************************** * Windows文件夹.文件源代码对比工具--WinM ...

  8. 词频统计V2.5

    一.前言 作业具体要求见[https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/922].一开始用JAVA写了个词频统计,然而没想出输入格式 ...

  9. Arch Linux 的休眠设置

    https://wiki.archlinux.org/index.php/Power_management/Suspend_and_hibernate_(简体中文)https://wiki.archl ...

  10. ANSYS耦合

    目录 定义 如何生成耦合自由度集 1.在给定节点处生成并修改耦合自由度集 2.耦合重合节点. 3.迫使节点有相同的表现方式 生成更多的耦合集 1. CPLGEN 2.CPSGEN 使用耦合注意事项 约 ...