Spring代理模式及AOP基本術語

1、代理模式:java

靜態代理、動態代理編程

動態代理和靜態代理區別??緩存

解析:靜態代理須要手工編寫代理類,代理類引用被代理對象。框架

        動態代理是在內存中構建的,不須要手動編寫代理類函數式編程

代理的目的:是爲了在原有的方法上進行加強。函數

動態代理的兩種方式:JDK動態代理與CGLIB代理
默認狀況下,Spring AOP的實現對於接口來講就是使用的JDK的動態代理來實現的,而對於類的代理使用CGLIB來實現。測試

2、靜態代理this

①定義接口 Subject並添加方法:spa

public interface Subject {
public void request();
}

② 定義接口的實現類 RealSubject並實現該接口,重寫方法--被代理對象代理

public class RealSubject implements Subject{
 
    public void request() {
        System.out.println("真實主題內容");
     
    }

 

③定義接口的實現類ProxySubject並實現該接口重寫方法。自定義屬性RealSubject,調用request方法,在這裏進行加強

public class ProxySubject implements Subject{
 
private RealSubject real;
     
public void request() {
    System.out.println("===before===");
    //調用request()
    real.request();
    System.out.println("===after===");
         
    }
 
 
    public RealSubject getReal() {
        return real;
    }
    public void setReal(RealSubject real) {
        this.real = real;
    }

 

④測試類: 

分別建立出被代理對象和代理對象,執行方法

public class ProxyTest {
//靜態代理
   @Test
   public void test01(){
    //準備一個真實主題,被代理對象
    RealSubject real=new RealSubject();
    //建立一個代理對象
    ProxySubject proxy=new ProxySubject();
     
    proxy.setReal(real);
    proxy.request();
}

 

 執行效果:

能夠看出靜態代理類有一個缺點:當若是接口加一個方法,全部的實現類和代理類裏都須要作個實現。這就增長了代碼的複雜度。動態代理就能夠避免這個缺點。 


3、動態代理

1.一、JDK動態代理:

本質:在內存中構建接口實現類

特色:被代理對象必須有接口 

 JDK提供了java.lang.reflect.Proxy類來實現動態代理的,可經過它的newProxyInstance來得到代理實現類。同時對於代理的接口的實際處理,是一個java.lang.reflect.InvocationHandler,它提供了一個invoke方法供實現者提供相應的代理邏輯的實現。能夠對實際的實現進行一些特殊的處理,像Spring AOP中的各類advice。下面來看看如何使用。

定義接口IUserDao:

public interface IUserDao {
 
public String add();
 
public String edit();
}

 

定義接口實現類,實現某接口,並重寫該方法:

public String add() {
    System.out.println("==add===");
    return "我是add";
     
}
 
public String edit() {
    System.out.println("===edit===");
    return "edit";
     
}

 

測試類:(重點)  

實現效果:

能夠由源碼可見如何生成代理類的:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException {
        if (h == null) {
            throw new NullPointerException();
        }
 
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        // 這裏是生成class的地方
        Class<?> cl = getProxyClass0(loader, intfs);
        // 使用咱們實現的InvocationHandler做爲參數調用構造方法來得到代理類的實例
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }
其中newInstance只是調用Constructor.newInstance來構造相應的代理類實例,這裏重點是看getProxyClass0這個方法的實現:
    private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { 
        // 代理的接口數量不能超過65535
        if (interfaces.length > 65535) { 
            throw new IllegalArgumentException("interface limit exceeded"); 
        } 
        // JDK對代理進行了緩存,若是已經存在相應的代理類,則直接返回,不然纔會經過ProxyClassFactory來建立代理 
        return proxyClassCache.get(loader, interfaces); 
    

 

能夠看到,動態生成的代理類有以下特性:
   1) 繼承了Proxy類,實現了代理的接口,因爲java不能多繼承,這裏已經繼承了Proxy類了,不能再繼承其餘的類,因此JDK的動態代理不支持對實現類的代理,只支持接口的代理。
   2) 提供了一個使用InvocationHandler做爲參數的構造方法。
   3) 生成靜態代碼塊來初始化接口中方法的Method對象,以及Object類的equals、hashCode、toString方法。
   4) 重寫了Object類的equals、hashCode、toString,它們都只是簡單的調用了InvocationHandler的invoke方法,便可以對其進行特殊的操做,也就是說JDK的動態代理還能夠代理上述三個方法。


 1.二、cglib動態代理:

JDK實現動態代理須要實現類經過接口定義業務方法,對於沒有接口的類,如何實現動態代理呢,這就須要cglib了。cglib採用了很是底層的字節碼技術,其原理是經過字節碼技術爲一個類建立子類,並在子類中採用方法攔截的技術攔截全部父類方法的調用  

 建立被代理的類:

public class UserService {
public void delete(){
    System.out.println("delete ok!!!");
}
}

 

測試類:(重點)  

該類實現了建立子類的方法與代理的方法。SuperClass方法經過入參即父類的字節碼,經過擴展父類的class來建立代理對象。intercept()方法攔截全部目標類方法的調用,obj表示目標類的實例,method爲目標類方法的反射對象,args爲方法的動態入參,proxy爲代理類實例。proxy.invoke(obj, args)經過代理類調用父類中的方法 !

 

實現效果:

 


4、AOP基本術語:

AOP Aspect Oriented Programming 面向切面編程

在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。

面向對象編程是從【靜態角度】考慮程序的結構,而面向切面編程是從【動態角度】考慮程序運行過程。
AOP底層,就是採用【動態代理】模式實現的。採用了兩種代理:JDK動態代理和CGLIB動態代理。

基本術語(一些名詞): 
(1)切面(Aspect)
切面泛指[*交叉業務邏輯*]。事務處理和日誌處理能夠理解爲切面。經常使用的切面有通知(Advice)與顧問(Advisor)。實際就是對主業務邏輯的一種加強。

(2)織入(Weaving)
織入是指將切面代碼插入到目標對象的過程。代理的invoke方法完成的工做,能夠稱爲織入。

(3) 鏈接點(JoinPoint) 
鏈接點是指能夠被切面織入的方法。一般業務接口的方法均爲鏈接點

(4)切入點(PointCut)
切入點指切面具體織入的方法
注意:被標記爲final的方法是不能做爲鏈接點與切入點的。由於最終的是不能被修改的,不能被加強的。

(5)目標對象(Target)
目標對象指將要被加強的對象。即包含主業務邏輯的類的對象。

(6)通知(Advice) 
通知是切面的一種實現,能夠完成簡單的織入功能。通知定義了加強代碼切入到目標代碼的時間點,是目標方法執行以前執行,仍是執行以後執行等。切入點定義切入的位置,通知定義切入的時間。

(7)顧問(Advisor)顧問是切面的另外一種實現,可以將通知以更爲複雜的方式織入到目標對象中,是將通知包裝爲更復雜切面的裝配器。

相關文章
相關標籤/搜索