Cglib动态代理

Cglib动态代理实现

Cglib动态代理代码实现

Cglib是一个开源框架,可以在maven项目的pom.xml中引用,通过Cglib可以实现不依赖于接口的动态代理模式。同JDK动态代理实现模式对比,Cglib依赖类继承和重写,JDK依赖接口实现和方法实现。下面用Java实现Cglib动态代理:

1.定义一个不基于实现接口的类:

1
2
3
4
5
public class MovieStar extends Star{
public void movieShow(){
System.out.println("执行演员功能");
}
}

2.实现MethodIntercepter接口:

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
public class CglibProxyHandler implements MethodInterceptor {
Object target=null;
public CglibProxyHandler(Object target){
this.target=target;
}
public Object getInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
//第一个参数 代理对象,动态生成,通过getInstance方法获取
//第二个参数 委托类方法,实现的MovieStar类对象
//第三个参数 方法参数,传递给委托类对象方法的参数
//第四个参数 代理方法的MethodProxy对象,委托类每个方法都对应一个MethodProxy对象
//methodProxy.invokeSuper方法实际调用MethodProxy对象中的invokeSuper方法,MethodProxy对象内部定义了一个静态内部类,保存了动态生成的代理类对象和委托类对象引用类型为FastClass(提供通过索引的方式定位到具体方法)。通过调用代理类对象中的invoke方法,需要传人方法索引,代理对象和方法参数。调用CGLIB$movieShow$0方法执行super.movieShow()。
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("准备开始执行代理类方法");
methodProxy.invokeSuper(o,objects);
System.out.println("已经执行完代理类方法");
return null;
}
}

3.客户端调用动态生成代理类:

1
2
3
4
5
6
7
8
public class Client  {
public static void main(String[] args){
MovieStar movieStar=new MovieStar();
CglibProxyHandler cglibProxyHandler=new CglibProxyHandler(movieStar);
MovieStar star=(MovieStar)cglibProxyHandler.getInstance();
star.movieShow();
}
}

4.输出结果:

1
2
3
准备开始执行代理类方法
执行演员功能
已经执行完代理类方法

Cglib动态代理源码阅读对比JDK动态代理

读源码相对还是比较抽象,笔者也主要阐述一些自己对Cglib动态代理源码的理解。其实无论是Cglib还是JDK动态代理实际上都是探讨如何设计这个动态生成的代理类,将所有的工作交给运行时过程去动态维护而不是人工维护。

Cglib动态代理与JDK动态代理基本区别

1.Cglib动态代理委托类不需要实现接口,扩展性更强,而JDK动态代理通过

1
Proxy.newProxyInstance(mtarget.getClass().getClassLoader(),mtarget.getClass().getInterfaces(),this)

的方式去反射出一个代理类就必然需要获取到委托类对象的接口信息。
2.Cglib动态代理不依赖于JDK的反射,而是定义了一个方法入口的索引,这样的实现方式较JDK动态代理的反射实现方式是高效的。
3.Cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法。

Cglib动态代理源码实现

不多说,先上图:
cmd-markdown-logo

动态生成的代理类实例
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
import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;

public class UserService$$EnhancerByCGLIB$$394dddeb extends UserService implements Factory
{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
//对应拦截器对象的引用
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$add$0$Method;
//对应委托类中的add方法
private static final MethodProxy CGLIB$add$0$Proxy;
private static final Object[] CGLIB$emptyArgs;


static void CGLIB$STATICHOOK2() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
final Class<?> forName = Class.forName("UserService$$EnhancerByCGLIB$$394dddeb");
final Class<?> forName3;
CGLIB$add$0$Method = ReflectUtils.findMethods(new String[] { "add", "()V" }, (forName3 = Class.forName("UserService")).getDeclaredMethods())[0];
CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0");
}
//动态生成的调用委托类中对应方法
final void CGLIB$add$0() {
super.add();
}
//暴露的动态代理服务
public final void add() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Method, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$emptyArgs, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Proxy);
return;
}
super.add();
}

static {
CGLIB$STATICHOOK2();
}
}
MethodProxy对象
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
public class MethodProxy {
private Signature sig1;
private Signature sig2;
private MethodProxy.CreateInfo createInfo;
private final Object initLock = new Object();
private volatile MethodProxy.FastClassInfo fastClassInfo;

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();
}
}
/*单看invokeSuper方法的实现,似乎看不出委托类add方法调用,在MethodProxy实现中,通过FastClassInfo维护了委托类和代理类的FastClass。*/

private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
}
}
/*以add方法的methodProxy为例,f1指向委托类对象,f2指向代理类对象,i1和i2分别是方法add和CGLIB$add$0在对象中索引位置*/
FastClass实现机制
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
/*FastClass其实就是对Class对象进行特殊处理,提出下标概念index,通过索引保存方法的引用信息,将原先的反射调用,转化为方法的直接调用,从而体现所谓的fast,下面通过一个例子了解一下FastClass的实现机制。*/
1、定义原类

class Test {
public void f(){
System.out.println("f method");
}

public void g(){
System.out.println("g method");
}
}

2、定义Fast类

class FastTest {
public int getIndex(String signature){
switch(signature.hashCode()){
case 3078479:
return 1;
case 3108270:
return 2;
}
return -1;
}

public Object invoke(int index, Object o, Object[] ol){
Test t = (Test) o;
switch(index){
case 1:
t.f();
return null;
case 2:
t.g();
return null;
}
return null;
}
}

/*在FastTest中有两个方法,getIndex中对Test类的每个方法根据hash建立索引,invoke根据指定的索引,直接调用目标方法,避免了反射调用。所以当调用methodProxy.invokeSuper方法时,实际上是调用代理类的CGLIB$add$0方法,CGLIB$add$0直接调用了委托类的add方法*/

总结

对于本篇Cglib动态代理源码的解读可能并没有很大的实际价值,笔者倒觉得这种实现方式源码逻辑封装得过于复杂。可以简单的理解Cglib动态生成的代理类是为每个委托类的方法维护了两个方法,一个是客户端调用入口方法,一个是通往其父委托类的入口,这样增大了字节码的复杂度,用空间换取了时间,实现了高效,同时不基于接口实现。

  • **JDK只能代理实现了接口的类,Cglib