1. 程式人生 > >Spring AOP的底層實現-代理模式

Spring AOP的底層實現-代理模式

在學習Spring的過程中,留下一下痕跡。
代理模式,其實就是讓別人做同樣的事情,但是別人卻不僅將我的事情做了,還會把他的事情也做了,換言之,這個人做的事情,是他獨有的事情和我需要做的事情的綜合。回到程式碼,那麼就是,代理類執行與委託類同樣的方法,在這方法裡代理類不僅可以執行委託類的方法的內容,還可以新增自己的另外的內容,在使用代理類的時候,會比直接使用委託類具有更多的能力,所以我們會直接使用代理類。
通常,代理類雖然具備更多的能力,但是代理類更多的能力其實是比較固定的。例如,在JDBC裡,或在ORM框架裡,都會存在事務的開啟和提交,如果我們直接將事務程式碼和業務程式碼寫在一起,其實也是可以的,不過,這樣做會使得每一個需要事務的方法,都要新增事務程式碼,造成程式碼的分散冗餘,那麼,針對這種情況,我們希望業務程式碼可以抽離出來,每一個業務方法都只寫業務內容,同時我們又希望,在執行業務方法時,可以有一種方式自動新增業務程式碼,這種需求,其實就是有了代理的需求,因為我們希望還是使用原來的方法,但是出來的效果希望是多出事務程式碼,也就是希望業務程式碼得到增強。可以這樣理解了,抽離出來的業務程式碼,是一個委託類,而可以將委託類自動增強並新增事務程式碼的程式碼,是一個代理類,它代理執行委託類,具備了委託類原有的業務能力之餘,增加了事務處理的程式碼。
我理解的代理模式,其實就是將委託類融入到代理類裡,換句話說,代理類也就是委託類的擴充而已。


還有不僅是spring 的AOP是代理模式,還有Struts2的攔截器實現也是基於動態代理的,動態代理是一種很常見也很重要的設計模式。
說了很多,那麼如何實現代理,那我們先說說靜態代理。

1.1 靜態代理

一個介面,兩個實現類,代理實現類組合真實實現類

靜態代理,是一種根據上面的理論,很自然會想到的一種不依賴於其他技術的代理模式實現方式。而他的實現過程如下圖。
這裡寫圖片描述

如果使用過靜態代理,那麼很容易理解,靜態代理存在的缺陷。

因此,也就出現了動態代理。

1.2 動態代理

JDK動態代理和CGlib位元組碼動態代理
1.2.1 JDK動態代理(必須有介面)

通過java.lang.reflect.Proxy類實現。
動態代理就是為了解決靜態代理不靈活的缺陷而產生的。靜態代理是固定的,一旦確定了程式碼,如果委託類新增一個方法,而這個方法又需要增強,那麼就必須在代理類裡重寫一個帶增強的方法。而動態代理可以靈活替換代理方法,動態就是體現在這裡
這裡寫圖片描述

public interface TargetClass {
    void sayHello();
}
public class TargetClassImpl implements TargetClass{
    public void sayHello() {
        System.out.println("你好"
); } }
public class JdkProxy implements InvocationHandler {
    private TargetClass targetClass;
    public Object createProxy(TargetClass targetClass){
        this.targetClass = targetClass;
        //獲取本類類載入器
        ClassLoader classLoader = JdkProxy.class.getClassLoader();
        ///獲取被代理物件的所有介面
        Class[] clazz = targetClass.getClass().getInterfaces();
        System.out.println(clazz.length);
        //生成代理類並返回
        return Proxy.newProxyInstance(classLoader, clazz, this);
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDKProxy前置增強");
        Object obj = method.invoke(targetClass,args);
        System.out.println("JDKProxy後置增強");
        return obj;
    }
}
public class Test {
    public static void main(String[] args) {
        JdkProxy jdkProxy = new JdkProxy();
        TargetClass targetClass = new TargetClassImpl();
        TargetClass targetClass1 = (TargetClass) jdkProxy.createProxy(targetClass);
        targetClass1.sayHello();
}
1.2.2 CGlib動態代理(不需要類繼承任何介面,位元組碼技術)

CGlib包在Spring core包裡。
這裡寫圖片描述

public class CGlibTaretClass {
    public void sayHello(){
        System.out.println("我是CGlib,我不需要介面");
    }
}
package CGlibProxyTest;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CGlibProxy implements MethodInterceptor{
    //代理方法
    public Object createProxy(Object target){
        //建立一個動態類物件
        Enhancer enhancer = new Enhancer();
        //確定要增強的類,設定期父類
        enhancer.setSuperclass(target.getClass());
        //添加回調函式
        enhancer.setCallback(this);
        //返回建立的代理類
        return enhancer.create();
    }
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGlib前置增強");
        Object obj = methodProxy.invokeSuper(o,objects);
        System.out.println("CGlib後置增強");
        return obj;
    }
}
public class Test {
    public static void main(String[] args) {
        CGlibProxy cGlibProxy = new CGlibProxy();
        CGlibTaretClass cGlibTaretClass = new CGlibTaretClass();
        CGlibTaretClass cGlibTaretClass1 = (CGlibTaretClass)cGlibProxy.createProxy(cGlibTaretClass);
        cGlibTaretClass1.sayHello();
   }
最後:代理模式解決的是常見的程式碼組織問題,它並不神祕,不要被他的名字嚇倒。