mybatis的懒加载是用到了javassist的动态代理,所以想先简单说一下这个,顺便带上cglib动态代理。
javassist动态代理
这里我用的依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.21.0-GA</version>
</dependency>
测试代码:
package lnstark.test.testjavassistproxy;
import javassist.NotFoundException;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* by Lnstark
* 2021/12/18
*/
public class MyProxyFactory implements MethodHandler {
private Object target;
public MyProxyFactory(Object target) {
this.target = target;
}
/**
* 增强方法
*
* @param self 代理后的类
* @param thisMethod 代理类的tweet方法
* @param proceed 代理类的_d8tweet方法
* @param args 方法参数
* @return 执行结果
*/
@Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
System.out.println("before invoke");
// 还记得jdk动态代理这里的传参会是target
Object ret = proceed.invoke(self, args);
System.out.println("after invoke");
return ret;
}
static class Bird {
private String name;
public Bird(String name) {
this.name = name;
}
void tweet() {
System.out.println("my name is: " + name);
}
}
public static void main(String[] args) throws IOException, NotFoundException {
Bird bird = new Bird("百灵");
Bird proxy = null;
ProxyFactory enhancer = new ProxyFactory();
// 设置代理类输出路径,实际的目录还要带上包名
// 如此类的包名为lnstark.test.testjavassistproxy
// 那生成的.class文件所在目录为 d:/lnstark/test/testjavassistproxy
enhancer.writeDirectory = "d:/";
// 需要代理的类
enhancer.setSuperclass(Bird.class);
try {
// 生成代理对象
proxy = (Bird) enhancer.create(new Class[]{String.class}, new Object[]{"布谷"}, new MyProxyFactory(bird));
} catch (NoSuchMethodException | InstantiationException
| IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
proxy.tweet();
}
}
执行结果:
before invoke
my name is: 布谷
after invoke
编译后的动态代理.class文件丢到idea里:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package lnstark.test.testjavassistproxy;
import java.io.ObjectStreamException;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyObject;
import javassist.util.proxy.RuntimeSupport;
import lnstark.test.testjavassistproxy.MyProxyFactory.Bird;
public class MyProxyFactory$Bird_$$_jvstae2_0 extends Bird implements ProxyObject {
private MethodHandler handler;
public static byte[] _filter_signature;
public static final long serialVersionUID;
private static Method[] _methods_;
public MyProxyFactory$Bird_$$_jvstae2_0(String var1) {
this.handler = RuntimeSupport.default_interceptor;
super(var1);
}
public final Object _d0clone() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
Method[] var1 = _methods_;
return (Object)this.handler.invoke(this, var1[0], var1[1], new Object[0]);
}
public final boolean _d1equals(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
Method[] var2 = _methods_;
return (Boolean)this.handler.invoke(this, var2[2], var2[3], new Object[]{var1});
}
public final void _d2finalize() throws Throwable {
super.finalize();
}
protected final void finalize() throws Throwable {
Method[] var1 = _methods_;
this.handler.invoke(this, var1[4], var1[5], new Object[0]);
}
public final int _d4hashCode() {
return super.hashCode();
}
public final int hashCode() {
Method[] var1 = _methods_;
return (Integer)this.handler.invoke(this, var1[8], var1[9], new Object[0]);
}
public final String _d7toString() {
return super.toString();
}
public final String toString() {
Method[] var1 = _methods_;
return (String)this.handler.invoke(this, var1[14], var1[15], new Object[0]);
}
public final void _d8tweet() {
super.tweet();
}
final void tweet() {
Method[] var1 = _methods_;
this.handler.invoke(this, var1[16], var1[17], new Object[0]);
}
static {
Method[] var0 = new Method[24];
Class var1 = Class.forName("lnstark.test.testjavassistproxy.MyProxyFactory$Bird_$$_jvstae2_0");
RuntimeSupport.find2Methods(var1, "clone", "_d0clone", 0, "()Ljava/lang/Object;", var0);
RuntimeSupport.find2Methods(var1, "equals", "_d1equals", 2, "(Ljava/lang/Object;)Z", var0);
RuntimeSupport.find2Methods(var1, "finalize", "_d2finalize", 4, "()V", var0);
RuntimeSupport.find2Methods(var1, "hashCode", "_d4hashCode", 8, "()I", var0);
RuntimeSupport.find2Methods(var1, "toString", "_d7toString", 14, "()Ljava/lang/String;", var0);
RuntimeSupport.find2Methods(var1, "tweet", "_d8tweet", 16, "()V", var0);
_methods_ = var0;
serialVersionUID = -1L;
}
public void setHandler(MethodHandler var1) {
this.handler = var1;
}
public MethodHandler getHandler() {
return this.handler;
}
Object writeReplace() throws ObjectStreamException {
return RuntimeSupport.makeSerializedProxy(this);
}
}
find2Methods方法:
public static void find2Methods(Class clazz, String superMethod,
String thisMethod, int index,
String desc, java.lang.reflect.Method[] methods)
{
methods[index + 1] = thisMethod == null ? null
: findMethod(clazz, thisMethod, desc);
methods[index] = findSuperClassMethod(clazz, superMethod, desc);
}
可以看出tweet是_methods_[16],_d8tweet是_methods_[17]。
代理类调用tweet方法,里面调用handler.invoke(即我们实现的方法)。
传入参数:代理类本身,tweet方法,_d8tweet方法和方法参数。
cglib动态代理
引入包:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
测试代码:
package lnstark.test.testcglibproxy;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* by Lnstark
* 2021/12/19
*/
public class CglibProxy {
static class Bird {
private String name;
public Bird(String name) {
this.name = name;
}
void tweet() {
System.out.println("my name is: " + name);
}
}
static class MyInterceptor implements MethodInterceptor {
/**
*
* @param o 代理后的对象
* @param method Bird的原方法
* @param objects 方法参数
* @param methodProxy
* @return 方法返回
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before invoke " + method.getName());
// 这里用methodProxy而不是原始方法method
Object ret = methodProxy.invokeSuper(o, objects);
System.out.println("after invoke " + method.getName());
return ret;
}
}
public static void main(String[] args) {
Bird bird = new Bird("百灵");
// 指定生成的.class文件目录
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:/");
// 生成的步骤类似javassist
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Bird.class);
enhancer.setCallback(new MyInterceptor());
Bird proxy = (Bird) enhancer.create(new Class[]{String.class}, new Object[]{"布谷"});
proxy.tweet();
}
}
结果:
before invoke tweet
my name is: 布谷
after invoke tweet
代理类里会生成下面两个方法,tweet方法里调用我们实现的intercept方法,里面执行了methodProxy.invokeSuper(o, objects);底层是调用的这里的CGLIB$tweet$0方法。
final void CGLIB$tweet$0() {
super.tweet();
}
final void tweet() {
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$tweet$0$Method, CGLIB$emptyArgs, CGLIB$tweet$0$Proxy);
} else {
super.tweet();
}
}