spring 中 AOP是基于 “動(dòng)態(tài)代理” 實(shí)現(xiàn),其采用了兩種方式:
- java代理:采用java內(nèi)置的代理API實(shí)現(xiàn)
- cglib代理:采用第三方API實(shí)現(xiàn)
本文主要闡述這兩種方式的區(qū)別
動(dòng)態(tài)代理和靜態(tài)代理
- 靜態(tài)代理:編譯時(shí)將增強(qiáng)代碼植入class文件,因?yàn)槭蔷幾g期進(jìn)行的增強(qiáng),所以代碼運(yùn)行時(shí)效率比動(dòng)態(tài)代理高。使用Aspect可以實(shí)現(xiàn)靜態(tài)代理苟翻。
- 動(dòng)態(tài)代理:運(yùn)行時(shí)生成代理類并加載,效率比靜態(tài)代理要低,spring中使用了上文中的兩種動(dòng)態(tài)代理的方式來實(shí)現(xiàn)代理類的生成发侵。
JDK 動(dòng)態(tài)代理
cglib代理類的實(shí)現(xiàn)效率是比 jdk代理類實(shí)現(xiàn)效率要高,并且更強(qiáng)大的妆偏,但spring中更推薦使用java原生的代理方式刃鳄,原因在于,第三方lib的實(shí)現(xiàn)有太多不確定性钱骂,使用java原生的代理方式更加有保障和穩(wěn)定叔锐。
java代理類的實(shí)現(xiàn)主要靠Proxy
類和InvocationHandler
接口來實(shí)現(xiàn)。下面直接拋出示例代碼demo:
/**
* jdk 代理的示例demo
*/
public class MyJdkProxy{
/**
* 接口
*/
public interface IHello{
void sayHello();
}
/**
* IHello接口實(shí)現(xiàn)類
*/
static class Hello implements IHello{
public void sayHello() {
System.out.println("Hello world!!");
}
}
/**
* 自定義InvocationHandler
*/
static class HWInvocationHandler implements InvocationHandler {
//目標(biāo)對(duì)象
private Object target;
public HWInvocationHandler(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代碼-------------");
//執(zhí)行相應(yīng)的目標(biāo)方法
Object rs = method.invoke(target,args);
System.out.println("------插入后置處理代碼-------------");
return rs;
}
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
/*
* 方式一:
*/
//獲取動(dòng)態(tài)代理類class并加載
Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);
//獲得代理類的構(gòu)造函數(shù)见秽,并傳入?yún)?shù)類型InvocationHandler.class
Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
//通過構(gòu)造函數(shù)來創(chuàng)建動(dòng)態(tài)代理對(duì)象掌腰,將自定義的InvocationHandler實(shí)例傳入
IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));
/*
* 方式二:可看成方式一的語法糖
*/
// IHello iHello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), //加載接口的類加載器
// new Class[]{IHello.class}, //一組接口
// new HWInvocationHandler(new Hello())); //自定義的InvocationHandler
//通過代理對(duì)象調(diào)用目標(biāo)方法
iHello.sayHello();
}
}
代理類的class是在Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class)
這個(gè)方法執(zhí)行后被生成,class路徑在項(xiàng)目根目錄的com\sun\proxy
中:
我們反編譯下這個(gè)代理類class:
public final class $Proxy0
extends Proxy
implements MyJdkProxy.IHello
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void sayHello()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("test.wsz.spring.aop.MyJdkProxy$IHello").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
從代理類class可以看出张吉,java生成的代理類實(shí)現(xiàn)了 被代理對(duì)象的接口IHello
齿梁,同時(shí)繼承了 Proxy
。
這個(gè)地方解決了我們的一個(gè)疑惑:spring為什么要采用兩種動(dòng)態(tài)代理的實(shí)現(xiàn)方式?
原因就在于勺择,JDK的動(dòng)態(tài)代理只能針對(duì)被接口修飾的類创南,因?yàn)榇眍?code>$Proxy0必須繼承Proxy
,Java中的類又是單繼承的省核,所以只能規(guī)定委托類Hello
必須有實(shí)現(xiàn)接口IHello
稿辙,從而使生成的代理類$Proxy0
能夠重寫委托了中的方法sayHello()
。
這里梳理下java中代理類的生成步驟:
- 通過
Proxy.getProxyClass()
方法生成代理類的class文件并加載气忠。(具體生成步驟見 源碼中ProxyClassFactory.apply()
) - 獲取這個(gè)class的構(gòu)造器邻储,傳入代理類的邏輯實(shí)現(xiàn)類
InvocationHandler
作為構(gòu)造函數(shù)參數(shù),實(shí)例化class獲得代理類對(duì)象旧噪。 - 調(diào)用代理類對(duì)象的對(duì)應(yīng)方法
具體源碼就不分析了吨娜,可看 深度剖析JDK動(dòng)態(tài)代理機(jī)制
CGLIB 動(dòng)態(tài)代理
不多說,直接上demo代碼:
/**
* cglib 代理的示例demo
*/
public class MyCglibProxy {
/**
* Hello類
*/
static class Hello{
public void sayHello() {
System.out.println("Hello world!!");
}
}
static class HelloMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("------插入前置通知代碼-------------");
//執(zhí)行相應(yīng)的目標(biāo)方法
Object rs = methodProxy.invokeSuper(o,objects);
System.out.println("------插入后置處理代碼-------------");
return rs;
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//代理類基礎(chǔ)自 被代理類Hello
enhancer.setSuperclass(Hello.class);
//設(shè)置回調(diào)類淘钟,代理邏輯在回調(diào)類中
enhancer.setCallback(new HelloMethodInterceptor());
//生成代理類
Hello hello = (Hello) enhancer.create();
hello.sayHello();
}
}
生成的代理類class:
public class MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc
extends MyCglibProxy.Hello
implements Factory
{
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$sayHello$0$Method;
private static final MethodProxy CGLIB$sayHello$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
/* Error */
static void CGLIB$STATICHOOK1()
{
}
final void CGLIB$sayHello$0()
{
super.sayHello();
}
public final void sayHello()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
if (this.CGLIB$CALLBACK_0 != null) {
return;
}
super.sayHello();
}
final boolean CGLIB$equals$1(Object paramObject)
{
return super.equals(paramObject);
}
public final boolean equals(Object paramObject)
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null)
{
Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$1$Method, new Object[] { paramObject }, CGLIB$equals$1$Proxy);
tmp41_36;
return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
}
return super.equals(paramObject);
}
final String CGLIB$toString$2()
{
return super.toString();
}
public final String toString()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null) {
return (String)tmp17_14.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);
}
return super.toString();
}
final int CGLIB$hashCode$3()
{
return super.hashCode();
}
public final int hashCode()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null)
{
Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
tmp36_31;
return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
}
return super.hashCode();
}
final Object CGLIB$clone$4()
throws CloneNotSupportedException
{
return super.clone();
}
protected final Object clone()
throws CloneNotSupportedException
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null) {
return tmp17_14.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);
}
return super.clone();
}
/* Error */
public static MethodProxy CGLIB$findMethodProxy(net.sf.cglib.core.Signature arg0)
{
// Byte code:
// 0: aload_0
// 1: invokevirtual 119 java/lang/Object:toString ()Ljava/lang/String;
// 4: dup
// 5: invokevirtual 120 java/lang/Object:hashCode ()I
// 8: lookupswitch default:+112->120, -508378822:+52->60, 1535311470:+64->72, 1826985398:+76->84, 1913648695:+88->96, 1984935277:+100->108
// 60: ldc 122
// 62: invokevirtual 123 java/lang/Object:equals (Ljava/lang/Object;)Z
// 65: ifeq +56 -> 121
// 68: getstatic 116 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$clone$4$Proxy Lnet/sf/cglib/proxy/MethodProxy;
// 71: areturn
// 72: ldc 125
// 74: invokevirtual 123 java/lang/Object:equals (Ljava/lang/Object;)Z
// 77: ifeq +44 -> 121
// 80: getstatic 49 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$sayHello$0$Proxy Lnet/sf/cglib/proxy/MethodProxy;
// 83: areturn
// 84: ldc 127
// 86: invokevirtual 123 java/lang/Object:equals (Ljava/lang/Object;)Z
// 89: ifeq +32 -> 121
// 92: getstatic 68 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$equals$1$Proxy Lnet/sf/cglib/proxy/MethodProxy;
// 95: areturn
// 96: ldc -127
// 98: invokevirtual 123 java/lang/Object:equals (Ljava/lang/Object;)Z
// 101: ifeq +20 -> 121
// 104: getstatic 85 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$toString$2$Proxy Lnet/sf/cglib/proxy/MethodProxy;
// 107: areturn
// 108: ldc -125
// 110: invokevirtual 123 java/lang/Object:equals (Ljava/lang/Object;)Z
// 113: ifeq +8 -> 121
// 116: getstatic 98 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$hashCode$3$Proxy Lnet/sf/cglib/proxy/MethodProxy;
// 119: areturn
// 120: pop
// 121: aconst_null
// 122: areturn
}
public MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc()
{
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
{
CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
{
CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
}
private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
{
1716bafc local1716bafc = (1716bafc)paramObject;
if (!local1716bafc.CGLIB$BOUND)
{
local1716bafc.CGLIB$BOUND = true;
Object tmp23_20 = CGLIB$THREAD_CALLBACKS.get();
if (tmp23_20 == null)
{
tmp23_20;
CGLIB$STATIC_CALLBACKS;
}
local1716bafc.CGLIB$CALLBACK_0 = (tmp31_28 == null ? tmp31_28 : (MethodInterceptor)((Callback[])tmp23_20)[0]);
}
}
public Object newInstance(Callback[] paramArrayOfCallback)
{
CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
CGLIB$SET_THREAD_CALLBACKS(null);
return new 1716bafc();
}
public Object newInstance(Callback paramCallback)
{
CGLIB$SET_THREAD_CALLBACKS(new Callback[] { paramCallback });
CGLIB$SET_THREAD_CALLBACKS(null);
return new 1716bafc();
}
/* Error */
public Object newInstance(Class[] arg1, Object[] arg2, Callback[] arg3)
{
// Byte code:
// 0: aload_3
// 1: invokestatic 195 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$SET_THREAD_CALLBACKS ([Lnet/sf/cglib/proxy/Callback;)V
// 4: new 2 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc
// 7: dup
// 8: aload_1
// 9: dup
// 10: arraylength
// 11: tableswitch default:+24->35, 0:+17->28
// 28: pop
// 29: invokespecial 196 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:<init> ()V
// 32: goto +17 -> 49
// 35: goto +3 -> 38
// 38: pop
// 39: new 202 java/lang/IllegalArgumentException
// 42: dup
// 43: ldc -52
// 45: invokespecial 207 java/lang/IllegalArgumentException:<init> (Ljava/lang/String;)V
// 48: athrow
// 49: aconst_null
// 50: invokestatic 195 test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$SET_THREAD_CALLBACKS ([Lnet/sf/cglib/proxy/Callback;)V
// 53: areturn
}
public Callback getCallback(int paramInt)
{
CGLIB$BIND_CALLBACKS(this);
switch (paramInt)
{
case 0:
break;
}
return null;
}
public void setCallback(int paramInt, Callback paramCallback)
{
switch (paramInt)
{
case 0:
this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
break;
}
}
public Callback[] getCallbacks()
{
CGLIB$BIND_CALLBACKS(this);
return new Callback[] { this.CGLIB$CALLBACK_0 };
}
public void setCallbacks(Callback[] paramArrayOfCallback)
{
this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramArrayOfCallback[0]);
}
static {}
}
從反編譯的class文件可看出宦赠,cglib生成的代理類是直接繼承被代理類Hello,代理的具體邏輯是通過回調(diào)類HelloMethodInterceptor
實(shí)現(xiàn)米母。
這里不詳細(xì)分析cglib的源碼勾扭,可看:深入理解CGLIB動(dòng)態(tài)代理機(jī)制
兩種動(dòng)態(tài)代理方式的區(qū)別
- cglib底層運(yùn)用了asm這個(gè)非常強(qiáng)大的Java字節(jié)碼生成框架來生成class, 比jdk代理效率要高
- jdk代理只能代理被接口修飾的類铁瞒,而cglib沒有這個(gè)限制(原因上面已經(jīng)分析妙色,jdk是動(dòng)態(tài)生成委托類的接口的實(shí)現(xiàn)類,cglib是動(dòng)態(tài)生成委托類的子類)
最后補(bǔ)充下:spring中慧耍,如果bean實(shí)現(xiàn)了接口身辨,則會(huì)使用jdk代理方式,否則采用cglib代理方式蜂绎。也可通過<aop:aspectj-autoproxy proxy-target-class="true"/>
配置來強(qiáng)制使用cglib代理方式