Spring AOP 的實現主要有兩種:CGLib與JDK自帶的Proxy。 他們主要的區別是,需要JDKProxy修改的類必須實現介面(因此也只能代理public方法),在創建Proxy時可以使用class.getInterfaces()獲得所有介面併進行代理。 而CGLib不受這個限制可以修改任 ...
Spring AOP 的實現主要有兩種:CGLib與JDK自帶的Proxy。
他們主要的區別是,需要JDKProxy修改的類必須實現介面(因此也只能代理public方法),在創建Proxy時可以使用class.getInterfaces()獲得所有介面併進行代理。
而CGLib不受這個限制可以修改任何非private非final方法。
以上只是一些大家都知道的方面。在這兩種方法的實現中,其實還是有其他一些重要的差別的,那就是調用代理類的方法內部同時調用了自己的另一個方法的話他們的最終結果將是不一樣的,下麵上代碼!
//被代理對象介面
public interface DummyInterface {
void fun2();
void fun1();
}
//被代理對象實現
public class Dummy implements DummyInterface {
public Dummy(){
}
public void fun1(){
System.out.println("fun1 start");
fun2(); //調用內部方法fun2
System.out.println("fun1 end");
}
public void fun2() {
System.out.println("-fun2 start");
System.out.println("-fun2 end");
}
}
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//CGLib實現
public class CGlibEnhance implements MethodInterceptor{
public static void main(String[] args) {
CGlibEnhance ce = new CGlibEnhance();
Dummy dummy = (Dummy) ce.getProxy(Dummy.class);//獲得代理對象
dummy.fun1();
System.out.println("----");
dummy.fun2();//直接調用fun2
}
public Object getProxy(Class<Dummy> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("pre-"+method.getName());
Object result = proxy.invokeSuper(obj,args);
System.out.println("post-"+method.getName());
return result;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//JDKProxy實現
public class JavaProxy implements InvocationHandler {
private Object target;//被代理對象
public JavaProxy(Object target) {
super();
this.target = target;
}
public static void main(String[] args) {
JavaProxy handler = new JavaProxy(new Dummy());
DummyInterface proxy = (DummyInterface) handler.getProxy();//獲得代理對象
proxy.fun1();
System.out.println("----");
proxy.fun2();//直接調用fun2
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("pre-" + method.getName());
result = method.invoke(target, args);
System.out.println("post-" + method.getName());
return result;
}
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
}
}
以上兩個方法都代理了Dummy對象並分別調用了fun1 fun2兩個方法,fun1內部又調用了fun2。
兩個結果卻不一樣
//cglib 輸出
pre-fun1
fun1 start
pre-fun2 //★
-fun2 start
-fun2 end
post-fun2 //★
fun1 end
post-fun1
----
pre-fun2
-fun2 start
-fun2 end
post-fun2
//JDKProxy輸出
pre-fun1
fun1 start
-fun2 start //★
-fun2 end //★
fun1 end
post-fun1
----
pre-fun2
-fun2 start
-fun2 end
post-fun2
可以看出在fun1調用fun2時(★處),JDKProxy並沒有截獲調用註入pre-fun2/post-fun2,但直接調用fun2時卻接獲到方法調用。
其中原因是CGLib是使用繼承的方式來改寫原類,同時也可以看到在CGLib中我們並沒有手動創建Dummy對象,因為CGLib create方法內部會自動創建。
而JDKProxy正如其名是Proxy,只是對原對象使用了代理模式,無法滲透到方法的內部調用。
這個從側面說明瞭在spring AOP中如果被代理類進行類內部調用時會導致無法註入方法的情況。