上篇博客介绍了JDK当中提供的动态代理实现方式,这篇主要是来介绍,通过CGLib工具实现的动态代理。
首先在maven项目当中添加jar包依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
接下来我们可以直接编写具体的实现类,不需要提供接口
public class HelloServiceImpl {
public void sayHello() {
System.out.println("hello eakonzhao");
}
}
接下来编写自定义的代理类,需要继承MethodInterceptor
public class HelloMethodInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before:" + method.getName());
Object object = methodProxy.invokeSuper(o,objects);
System.out.println("After:" + method.getName());
return object;
}
}
测试类
public class Client {
public static void main(String[] args) {
// 设置动态代理生成的class文件路径
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\Users\\Administrator\\Desktop\\");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloServiceImpl.class);
enhancer.setCallback(new HelloMethodInterceptor());
HelloServiceImpl helloService = (HelloServiceImpl)enhancer.create();
helloService.sayHello();
}
}
执行结果
Before:sayHello
hello eakonzhao
After:sayHello
我们发现这个CGLib动态代理生成了三个class文件
这个类继承了我们前面设置的HelloServiceImpl类,实现了Factory接口
我们主要看HelloServiceImpl$$EnchancerByCGLIB$$这个类,这个类当中生成了一个final的类型的sayHello方法,还有一个CGLIB$sayHello$0
public final void sayHello() {
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$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
} else {
super.sayHello();
}
}
final void CGLIB$sayHello$0() {
super.sayHello();
}
这里的this.CGLIB$CALLBACK_0就是我们传递进去的HelloMethodInterceptor对象,如果为null的话,可以看到是直接调用父类的sayHello方法,否则就调用HelloMethodInterceptor当中的intercept方法。
当我们调用自定义的HelloMethodInterceptor方法的intercept时,会执行我们添加进去的逻辑,调用MethodProxy.invokeSuper方法,来调用我们自己类的实现方法,接下来看MethodProxy当中invokeSuper的实现
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();
}
}
我们来看下init方法
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();
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;
}
}
}
}
这一步主要是完成对FastClassInfo的初始化操作,关于FastClassInfo,其结构如下
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {
}
}
f1主要保存的是代理对象的类,即HelloMethodInterceptor对象,f2是被代理对象的类信息即HelloServiceImpl,i1是代理类方法签名索引,i2是被代理类方法签名索引
FastClass的实现类就是我们上面截图当中生成的
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1725733088:
if(var10000.equals("getClass()Ljava/lang/Class;")) {
return 7;
}
break;
case -1026001249:
if(var10000.equals("wait(JI)V")) {
return 2;
}
break;
case 243996900:
if(var10000.equals("wait(J)V")) {
return 3;
}
break;
case 946854621:
if(var10000.equals("notifyAll()V")) {
return 9;
}
break;
case 1116248544:
if(var10000.equals("wait()V")) {
return 1;
}
break;
case 1535311470:
if(var10000.equals("sayHello()V")) {
return 0;
}
break;
case 1826985398:
if(var10000.equals("equals(Ljava/lang/Object;)Z")) {
return 4;
}
break;
case 1902039948:
if(var10000.equals("notify()V")) {
return 8;
}
break;
case 1913648695:
if(var10000.equals("toString()Ljava/lang/String;")) {
return 5;
}
break;
case 1984935277:
if(var10000.equals("hashCode()I")) {
return 6;
}
}
return -1;
}
生成的getIndex方法,根据方法签名的hash值来返回索引,sayHello方法返回索引为0,
在进行方法调用时,根据索引调用相应的方法。CGLib是在invoke时,会生成FastClass对象,我们在调用的时候,其实调用的是已经生成好的方法,
可以看到其实调用的被代理类的方法,即CGLIB$sayHello$0 方法,而fci.f1对象的其实是HelloServiceImpl$$EnchancerByCGLIB$$当中的 sayHello方法。
对比CGLib和JDK当中的动态代理
JDK动态代理:
1、被代理对象必须实现一个接口,而CGLib则不需要,可以是单独一个类,也可以是一个接口的实现
2、JDK动态代理生成的文件比较简单,而CGLib生成的字节码文件更复杂,
3、在方法调用上,JDK采用反射的方式进行方法调用,而CGLib是直接调用生成好的方法,执行效率更高。