jdk动态代理
实例:动态代理有两个对象,目标对象和代理对象。使用JDK动态代理,目标对象必须实现一个接口。
public class JdkDynamicProxyTest {
interface IPerson {
void say() ;
}
static class Person implements IPerson {
@Override
public void say() {
System.out.println("hello world");
}
}
static class JdkProxy implements InvocationHandler {
//目标对象
private Object target;
//得到代理对象
public Object getProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("say before");
method.invoke(target, args);
System.out.println("say after");
return null;
}
}
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
JdkProxy jdkProxy = new JdkProxy();
IPerson person = (IPerson) jdkProxy.getProxy(new Person());
person.say();
}
}
原理
System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);会在项目路径下生成一个$Proxy0.class文件,反编译这个文件,就能够明白JDK动态代理原理。
final class $Proxy0 extends Proxy implements IPerson {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void say() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.ming.aop.IPerson").getMethod("say");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
代理的大概结构包括4部分:
静态字段:被代理的接口所有方法都有一个对应的静态变量;
静态块:主要是通过反射初始化静态变量;
具体每个代理方法:逻辑都差不多就是 h.invoke,h是Proxy的属性。主要是调用我们定义好的invocatinoHandler逻辑,触发目标对象target上对应的方法;
类结构如下:
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
}
构造函数:从这里传入我们InvocationHandler逻辑(如上的JdkProxy );构造函数在newProxyInstance方法中通过反射调用
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
cglib动态代理
cglib使用需要实现MethodInterceptor接口,与JDK动态代理不同的是,cglib不需要目标类实现接口。
public class CglibDynamicProxyTest {
static class Person {
public void say() {
System.out.println("hello world");
}
}
static class CglibProxy implements MethodInterceptor {
Object target;
public Object getProxy(Object target) {
this.target = target;
final Enhancer enhancer = new Enhancer();
enhancer.setUseFactory(false);
enhancer.setSuperclass(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("say before");
methodProxy.invokeSuper(o, objects);
System.out.println("say after");
return null;
}
}
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:\\gitRepository\\demo");
CglibProxy cglibProxy = new CglibProxy();
Person person = (Person) cglibProxy.getProxy(new Person());
person.say();
}
}
原理
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,“G:\gitRepository\demo”);会在配置的路径下会生成三个字节码文件,其中有两个继承FastClass类的是索引文件,还有一个继承person对象,是我们要研究的。
public class CglibDynamicProxyTest$Person$$EnhancerByCGLIB$$681783b8 extends Person {
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;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.ming.test.CglibDynamicProxyTest$Person$$EnhancerByCGLIB$$681783b8");
Class var1;
CGLIB$say$0$Method = ReflectUtils.findMethods(new String[]{"say", "()V"}, (var1 = Class.forName("com.ming.test.CglibDynamicProxyTest$Person")).getDeclaredMethods())[0];
CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$0");
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$toString$3$Method = var10000[2];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[4];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
}
final void CGLIB$say$0() {
super.say();
}
public final void say() {
//1. 得到MethodInterceptor,我们实现的接口,通过enhancer.setCallback(this)设置的。
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
//2. 调用代理的逻辑
if (var10000 != null) {
var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
} else {
super.say();
}
}
//..省略一些方法
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
CglibDynamicProxyTest$Person$$EnhancerByCGLIB$$681783b8 var1 = (CglibDynamicProxyTest$Person$$EnhancerByCGLIB$$681783b8)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
static {
CGLIB$STATICHOOK1();
}
}
代理类继承了目标类,并重写目标类方法。代理的大概结构包括三大块:
静态字段:目标类中所有方法(包括父类中的方法,Object除外)以及Object中finalize、equals、toString、hashCode、clone方法,都对应一个Method和MethodProxy静态变量。
静态代码块:静态代码块中调用CGLIB$STATICHOOK1()静态方法,这个方法主要是通过反射初始化Method静态变量,并初始化MethodProxy静态变量。Method表示目标类的方法,MethodProxy表示代理类中方法。
目标类中的方法:代理类中有两个方法,一个是重写目标类方法,重写的逻辑也比较简单,得到MethodInterceptor对象,然后调用intercept方法,intercept方法是我们实现的方法。还有一个CGLIBsay0方法,这个是给MethodProxy调用,实现逻辑直接调用目标类方法,在调用methodProxy.invokeSuper(o,
objects)时会调用这个方法。
注意如下的代码会发生了死循环。
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("say before");
method.invoke(o, objects);
System.out.println("say after");
return null;
}
我们从反编译的代码中var10000.intercept(this, CGLIB$say 0 0 0Method, CGLIB e m p t y A r g s , C G L I B emptyArgs, CGLIB emptyArgs,CGLIBsay 0 0 0Proxy)中可以看出,o表示的是代理类,method表示的是目标类中方法,但是因为代理类重写了目标类中方法,所以还是调用代理类中的方法。代理的方法又会调用intercept,就形成了死循环。
jdk动态代理类与cglib代理区别
jdk动态代理目标类必须实现一个接口,而cglib不需要。是因为jdk代理会继承Proxy类,而java又是单继承。
jdk代理类与目标类实现相同的接口,cglib代理直接继承目标类。
jdk和cglib都是把实现逻辑代理给接口。jdk代理给InvocationHandler接口,cglib代理给MethodInterceptor接口。
cglib一个目标类方法会生成两个代理方法,一个重写目标方法,并实现代理逻辑,还有一个直接调用目标类方法。