先来谈谈JDK动态代理
需求背景:
一电商项目的程序猿辛辛苦苦写完支付接口并测试成功且上线。这个时候项目经理跑过来提了一个需求,需要在客户支付之前记录客户支付日志,方便日后进行统计支付数据。
使用JDK动态代理解决上面的需求
为了不影响到之前写好的支付接口,不破坏接口的开闭原则。我们通过PayProxy绑定原先的支付接口,来解决这问题。
pay.java
public interface Pay {
public void doPay();
}
AliPay.java
public class AliPay implements Pay{
@Override
public void doPay() {
System.out.println("do AliPay");
}
}
PayProxy.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class PayProxy implements InvocationHandler {
private Object target;
public Object bind(Object target){
this.target = target;
return Proxy.
newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public void before() {
System.out.println("do something before pay");
}
public void after() {
System.out.println("do something after pay");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
}
PayCenter.java
public class PayCenter {
public static void main(String[] args) {
Pay aliPay = (Pay)new PayProxy().bind(new AliPay());
aliPay.doPay();
}
}
输出
do something before pay
do AliPay
do something after pay
简单阐述JDK动态代理原理
思考:
为什么现在的Pay接口调用的doPay()会打印before和after方法?现在Pay还是原先的Pay接口吗?
aliPay = {$Proxy0@528} "com.zjqx.proxy.jdk.AliPay@6979e8cb"
通过调试可以发现,现在的Pay已经不是我们原先创建的Pay接口了;而是 P r o x y 0 @ 528 这 个 对 象 ; 为 了 更 好 的 理 解 J D K 动 态 代 理 的 原 理 , 我 们 可 以 打 印 出 Proxy0@528这个对象;为了更好的理解JDK动态代理的原理,我们可以打印出 Proxy0@528这个对象;为了更好的理解JDK动态代理的原理,我们可以打印出Proxy0@528对象的反编译代码;
$Proxy0.class反编译后的文件
import com.sun.proxy.$Proxy0;
import java.lang.reflect.*;
public final class $Proxy extends Proxy
implements $Proxy0
{
public $Proxy(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final InvocationHandler getInvocationHandler(Object obj)
throws IllegalArgumentException
{
try
{
return (InvocationHandler)super.h.invoke(this, m5, new Object[] {
obj
});
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final Class getProxyClass(ClassLoader classloader, Class aclass[])
throws IllegalArgumentException
{
try
{
return (Class)super.h.invoke(this, m6, new Object[] {
classloader, aclass
});
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final Class getClass()
{
try
{
return (Class)super.h.invoke(this, m11, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void notifyAll()
{
try
{
super.h.invoke(this, m13, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void wait()
throws InterruptedException
{
try
{
super.h.invoke(this, m8, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void doPay()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void notify()
{
try
{
super.h.invoke(this, m12, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final Object newProxyInstance(ClassLoader classloader, Class aclass[], InvocationHandler invocationhandler)
throws IllegalArgumentException
{
try
{
return (Object)super.h.invoke(this, m7, new Object[] {
classloader, aclass, invocationhandler
});
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void wait(long l)
throws InterruptedException
{
try
{
super.h.invoke(this, m10, new Object[] {
Long.valueOf(l)
});
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final boolean isProxyClass(Class class1)
{
try
{
return ((Boolean)super.h.invoke(this, m4, new Object[] {
class1
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void wait(long l, int i)
throws InterruptedException
{
try
{
super.h.invoke(this, m9, new Object[] {
Long.valueOf(l), Integer.valueOf(i)
});
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m5;
private static Method m2;
private static Method m6;
private static Method m11;
private static Method m13;
private static Method m0;
private static Method m8;
private static Method m3;
private static Method m12;
private static Method m7;
private static Method m10;
private static Method m4;
private static Method m9;
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m5 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getInvocationHandler", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m6 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getProxyClass", new Class[] {
Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;")
});
m11 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getClass", new Class[0]);
m13 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notifyAll", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m8 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[0]);
m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("doPay", new Class[0]);
m12 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notify", new Class[0]);
m7 = Class.forName("com.sun.proxy.$Proxy0").getMethod("newProxyInstance", new Class[] {
Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler")
});
m10 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[] {
Long.TYPE
});
m4 = Class.forName("com.sun.proxy.$Proxy0").getMethod("isProxyClass", new Class[] {
Class.forName("java.lang.Class")
});
m9 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[] {
Long.TYPE, Integer.TYPE
});
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
提取关键代码
public $Proxy(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final void doPay()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("doPay", new Class[0]);
通过上述的代码可以看出,$Proxy对象是JVM自动生成的对象,实现了Pay接口,并重写了接口里的所有方法;
我们提取dopay方法可以看出,实际调用的是invocationhandler.invoke方法,这正是我们之前PayProxy类中的invoke方法,通过JDK的反射技术调用实际Object target即AliPay的dopay方法。
最后给出JDK动态代理简单的时序图(有点丑,见谅,哈哈哈)

JDK动态代理的简单总结:
- JDK动态代理需要被代理类实现接口,才能实现动态代理;
- JDK动态代理每次调用均是通过反射技术实现,在一定程度上影响性能;
CGLib动态代理
区别于JDK动态代理,CGLib动态代理的被代理类则不需要实现固定的接口;CGLib的动态代理会自动生成被代理类的子类,并重写父类的方法,从而实现代理;
实现代码
CglibProxy.java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
public Object getInstance(Object target){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
//回调intercept方法
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
public void before(){
System.out.println("Before");
}
public void after(){
System.out.println("After");
}
}
AliPay.java
public class AliPay {
public void doPay(){
System.out.println("alipay");
}
}
CglibTest.java
import net.sf.cglib.core.DebuggingClassWriter;
public class CglibTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G://cglib_folder");
AliPay alipay = (AliPay)new CglibProxy().getInstance(new AliPay());
alipay.doPay();
}
}
输出
before
doPay
after
简单阐述CgLib动态代理原理
我们通过以下代码输出CgLib的class文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G://cglib_folder");
进入G://cglib_folder会发现有三个类

通过调试我们发现:
alipay = {AliPay$$EnhancerByCGLIB$$e2467acf@671} "com.tomi.proxy.cglib.AliPay$$EnhancerByCGLIB$$e2467acf@3c0ecd4b"
AliPayEnhancerByCGLIBe2467acf.class文件正是Cglib动态获取的代理类,反编译该class文件;
public class AliPay$$EnhancerByCGLIB$$e2467acf extends AliPay
implements Factory
{
final void CGLIB$doPay$0()
{
super.doPay();
}
public final void doPay()
{
CGLIB$CALLBACK_0;
if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
JVM INSTR pop ;
CGLIB$BIND_CALLBACKS(this);
CGLIB$CALLBACK_0;
_L2:
JVM INSTR dup ;
JVM INSTR ifnull 37;
goto _L3 _L4
_L3:
break MISSING_BLOCK_LABEL_21;
_L4:
break MISSING_BLOCK_LABEL_37;
this;
CGLIB$doPay$0$Method;
CGLIB$emptyArgs;
CGLIB$doPay$0$Proxy;
intercept();
return;
super.doPay();
return;
}
}
CgLib代理类继承了AliPay类,并重写了父类的所有方法,调用doPay方法时会调用CGLIB$doPay$0方法,该方法正是父类的doPay方法;接着调用了intercept()方法。
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
进入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();
}
}
查看MethodProxy.FastClassInfo可以看到
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {
}
}
这里的f1和f2便是上述G://cglib_folder目录下的
- AliPay__EnhancerByCGLIB__e2467acf__FastClassByCGLIB__9697d901.class(代理类的FastClass)
- AliPay__FastClassByCGLIB__bf557141.class(被代理类的FastClass)
总结
- CgLib动态代理类之所以执行效率比JDK动态代理类快,是因为其FastClass机制,直接通过生成的FastClass类执行对应的方法;
- CgLib动态代理需要生成三个class类,所以比JDK动态代理生成时效率要慢;
最后附上一份手写JDK动态代理的git地址
https://github.com/TomiCat/DynamicProxyForMyself
此文章为本人平时学习整理巩固所写,如有错误,欢迎大家纠正~谢谢!
907

被折叠的 条评论
为什么被折叠?



