面对没有接口的类,可以使用基于cglib的动态代理,对于final方法无法代理。
cglib的底层是使用字节码处理框架asm操纵字节码生成代理类,操纵的级别是底层JVM的汇编指令级别。
使用案例:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
public class CglibTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PlayGame.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before method run...");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after method run...");
return result;
}
});
PlayGame sample = (PlayGame) enhancer.create();
sample.say("hahha");
}
}
如同Proxy,上述代码同时也生成另一个Class文件:CglibTest$1.class,实现MethodInterceptor接口。
原理
其实和Proxy的原理类似,但不同于Proxy代理类继承的是Proxy,用cglib生成的代理类继承的是被代理类,实现的是Factory接口。
我们可以在代码前加上System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://");
即可以在指定位置生成代理类。它会生成三个class文件:
从上到下依次是1.代理类的fastclass 2.代理类 3.被代理类的fastclass
查看代理类反编译的代码:
public class PlayGame$$EnhancerByCGLIB$$74aa30cd extends PlayGame 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$say$0$Method;
private static final MethodProxy CGLIB$say$0$Proxy;
............
//最底下静态代码块调用这个方法初始化
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.baiye.proxy.PlayGame$$EnhancerByCGLIB$$74aa30cd");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = var10000[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = var10000[1];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
........
CGLIB$say$0$Method = ReflectUtils.findMethods(new String[]{"say", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.baiye.proxy.PlayGame")).getDeclaredMethods())[0];
CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "say", "CGLIB$say$0");
}
final void CGLIB$say$0(String var1) {
super.say(var1);
}
public final void say(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
//调用拦截器
var10000.intercept(this, CGLIB$say$0$Method, new Object[]{var1}, CGLIB$say$0$Proxy);
} else {
super.say(var1);
}
}
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1853413644:
if (var10000.equals("say(Ljava/lang/String;)V")) {
return CGLIB$say$0$Proxy;
}
break;
case -1574182249:
if (var10000.equals("finalize()V")) {
return CGLIB$finalize$1$Proxy;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$5$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$2$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$3$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$4$Proxy;
}
}
return null;
}
public PlayGame$$EnhancerByCGLIB$$74aa30cd() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
PlayGame$$EnhancerByCGLIB$$74aa30cd var1 = (PlayGame$$EnhancerByCGLIB$$74aa30cd)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (var10000 == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
....
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
PlayGame$$EnhancerByCGLIB$$74aa30cd var10000 = new PlayGame$$EnhancerByCGLIB$$74aa30cd;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
.....
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
代理类会获得所有在父类继承来的方法,其中private MethodInterceptor CGLIB$CALLBACK_0;
属性是关键,代理类持有它然后调用var10000.intercept(this, CGLIB$say$0$Method, new Object[]{var1}, CGLIB$say$0$Proxy);
通过fastclass文件确定方法。
被代理类的class反编译:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.baiye.proxy;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
public class PlayGame$$FastClassByCGLIB$$5f92a78b extends FastClass {
public PlayGame$$FastClassByCGLIB$$5f92a78b(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1853413644:
if (var10000.equals("say(Ljava/lang/String;)V")) {
return 0;
}
break;
。。。。。。
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 6;
}
}
return -1;
}
public int getIndex(String var1, Class[] var2) {
switch(var1.hashCode()) {
case -1776922004:
if (var1.equals("toString")) {
switch(var2.length) {
case 0:
return 5;
}
}
break;
。。。。。
case 113643:
if (var1.equals("say")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.String")) {
return 0;
}
}
}
break;
。。。。。。
case 1950568386:
if (var1.equals("getClass")) {
switch(var2.length) {
case 0:
return 7;
}
}
}
return -1;
}
public int getIndex(Class[] var1) {
switch(var1.length) {
case 0:
return 0;
default:
return -1;
}
}
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
PlayGame var10000 = (PlayGame)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.say((String)var3[0]);
return null;
case 1:
var10000.wait();
return null;
。。。。。。
case 9:
var10000.notifyAll();
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
PlayGame var10000 = new PlayGame;
PlayGame var10001 = var10000;
int var10002 = var1;
try {
switch(var10002) {
case 0:
var10001.<init>();
return var10000;
}
} catch (Throwable var3) {
throw new InvocationTargetException(var3);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public int getMaxIndex() {
return 9;
}
}
代理类的class反编译和它差不多代码过多,不在展示。
通过观察其他两个FastClass文件可以知道,通过方法索引我们可以匹配到想要的方法并且调用,而不需要像 JDK 动态代理一样通过反射的方式调用,极大提高了执行效率。它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。
这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。
JDK动态代理和Gglib动态代理的区别:
1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。