Spring 核心 AOP 及 IOC 詳解

IOC : Inversion of Control

(1). IoC(Inversion of Control)是指容器控制程序?qū)ο笾g的關(guān)系燕偶,而不是傳統(tǒng)實(shí)現(xiàn)中景埃,由程序代碼直接操控”允控制權(quán)由應(yīng)用代碼中轉(zhuǎn)到了外部容器,控制權(quán)的轉(zhuǎn)移是所謂反轉(zhuǎn)。 對(duì)于Spring而言砖瞧,就是由Spring來控制對(duì)象的生命周期和對(duì)象之間的關(guān)系;IoC還有另外一個(gè)名字——“依賴注入(Dependency Injection)”。從名字上理解弥搞,所謂依賴注入她按,即組件之間的依賴關(guān)系由容器在運(yùn)行期決定,即由容器動(dòng)態(tài)地將某種依賴關(guān)系注入到組件之中竭翠。

(2). 在Spring的工作方式中,所有的類都會(huì)在spring容器中登記薇搁,告訴spring這是個(gè)什么東西斋扰,你需要什么東西,然后spring會(huì)在系統(tǒng)運(yùn)行到適當(dāng)?shù)臅r(shí)候啃洋,把你要的東西主動(dòng)給你传货,同時(shí)也把你交給其他需要你的東西。所有的類的創(chuàng)建宏娄、銷毀都由 spring來控制问裕,也就是說控制對(duì)象生存周期的不再是引用它的對(duì)象,而是spring孵坚。對(duì)于某個(gè)具體的對(duì)象而言僻澎,以前是它控制其他對(duì)象,現(xiàn)在是所有對(duì)象都被spring控制十饥,所以這叫控制反轉(zhuǎn)窟勃。

(3). 在系統(tǒng)運(yùn)行中,動(dòng)態(tài)的向某個(gè)對(duì)象提供它所需要的其他對(duì)象逗堵。

(4). 依賴注入的思想是通過反射機(jī)制實(shí)現(xiàn)的秉氧,在實(shí)例化一個(gè)類時(shí),它通過反射調(diào)用類中set方法將事先保存在HashMap中的類屬性注入到類中蜒秤。 總而言之汁咏,在傳統(tǒng)的對(duì)象創(chuàng)建方式中亚斋,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實(shí)例,而在Spring中創(chuàng)建被調(diào)用者的工作由Spring來完成攘滩,然后注入調(diào)用者帅刊,即所謂的依賴注入or控制反轉(zhuǎn)。 注入方式有兩種:依賴注入和設(shè)置注入漂问; IoC的優(yōu)點(diǎn):降低了組件之間的耦合赖瞒,降低了業(yè)務(wù)對(duì)象之間替換的復(fù)雜性,使之能夠靈活的管理對(duì)象蚤假。

AOP(Aspect Oriented Programming)

(1). AOP面向方面編程基于IoC栏饮,是對(duì)OOP的有益補(bǔ)充;

(2). AOP利用一種稱為“橫切”的技術(shù)磷仰,剖解開封裝的對(duì)象內(nèi)部袍嬉,并將那些影響了 多個(gè)類的公共行為封裝到一個(gè)可重用模塊,并將其名為“Aspect”灶平,即方面伺通。所謂“方面”,簡(jiǎn)單地說逢享,就是將那些與業(yè)務(wù)無關(guān)泵殴,卻為業(yè)務(wù)模塊所共同調(diào)用的 邏輯或責(zé)任封裝起來,比如日志記錄拼苍,便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度调缨,并有利于未來的可操作性和可維護(hù)性疮鲫。

(3). AOP代表的是一個(gè)橫向的關(guān) 系,將“對(duì)象”比作一個(gè)空心的圓柱體弦叶,其中封裝的是對(duì)象的屬性和行為俊犯;則面向方面編程的方法,就是將這個(gè)圓柱體以切面形式剖開伤哺,選擇性的提供業(yè)務(wù)邏輯燕侠。而 剖開的切面,也就是所謂的“方面”了立莉。然后它又以巧奪天功的妙手將這些剖開的切面復(fù)原绢彤,不留痕跡,但完成了效果蜓耻。

(4). 實(shí)現(xiàn)AOP的技術(shù)茫舶,主要分為兩大類:一是采用動(dòng)態(tài)代理技術(shù),利用截取消息的方式刹淌,對(duì)該消息進(jìn)行裝飾饶氏,以取代原有對(duì)象行為的執(zhí)行讥耗;二是采用靜態(tài)織入的方式,引入特定的語法創(chuàng)建“方面”疹启,從而使得編譯器可以在編譯期間織入有關(guān)“方面”的代碼古程。

(5). Spring實(shí)現(xiàn)AOP:JDK動(dòng)態(tài)代理和CGLIB代理 JDK動(dòng)態(tài)代理:其代理對(duì)象必須是某個(gè)接口的實(shí)現(xiàn),它是通過在運(yùn)行期間創(chuàng)建一個(gè)接口的實(shí)現(xiàn)類來完成對(duì)目標(biāo)對(duì)象的代理喊崖;其核心的兩個(gè)類是InvocationHandler和Proxy挣磨。 CGLIB代理:實(shí)現(xiàn)原理類似于JDK動(dòng)態(tài)代理,只是它在運(yùn)行期間生成的代理對(duì)象是針對(duì)目標(biāo)類擴(kuò)展的子類贷祈。CGLIB是高效的代碼生成包趋急,底層是依靠ASM(開源的java字節(jié)碼編輯類庫)操作字節(jié)碼實(shí)現(xiàn)的,性能比JDK強(qiáng)势誊;需要引入包asm.jar和cglib.jar呜达。 使用AspectJ注入式切面和@AspectJ注解驅(qū)動(dòng)的切面實(shí)際上底層也是通過動(dòng)態(tài)代理實(shí)現(xiàn)的。

(6). AOP使用場(chǎng)景:

Authentication 權(quán)限檢查

Caching 緩存

Context passing 內(nèi)容傳遞

Error handling 錯(cuò)誤處理

Lazy loading 延遲加載

Debugging  調(diào)試

logging, tracing, profiling and monitoring 日志記錄粟耻,跟蹤查近,優(yōu)化,校準(zhǔn)

Performance optimization 性能優(yōu)化挤忙,效率檢查

Persistence  持久化

Resource pooling 資源池

Synchronization 同步

Transactions 事務(wù)管理

代理模式:
代理(Proxy)是一種設(shè)計(jì)模式,提供了對(duì)目標(biāo)對(duì)象另外的訪問方式;即通過代理對(duì)象訪問目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.
這里使用到編程中的一個(gè)思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴(kuò)展該方法霜威。

代理模式的關(guān)鍵點(diǎn)是:代理對(duì)象與目標(biāo)對(duì)象.代理對(duì)象是對(duì)目標(biāo)對(duì)象的擴(kuò)展,并會(huì)調(diào)用目標(biāo)對(duì)象。

1.1.靜態(tài)代理
靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類.

/**
 * 接口
 */
public interface IUserDao {
    void save();
}

/**
 * 接口實(shí)現(xiàn)
 * 目標(biāo)對(duì)象
 */
public class UserDao implements IUserDao {
    public void save() {
        System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
    }
}

/**
 * 代理對(duì)象,靜態(tài)代理
 */
public class UserDaoProxy implements IUserDao{
    //接收保存目標(biāo)對(duì)象
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target=target;
    }

    public void save() {
        System.out.println("開始事務(wù)...");
        target.save();//執(zhí)行目標(biāo)對(duì)象的方法
        System.out.println("提交事務(wù)...");
    }
}

/**
 * 測(cè)試類
 */
public class App {
    public static void main(String[] args) {
        //目標(biāo)對(duì)象
        UserDao target = new UserDao();

        //代理對(duì)象,把目標(biāo)對(duì)象傳給代理對(duì)象,建立代理關(guān)系
        UserDaoProxy proxy = new UserDaoProxy(target);

        proxy.save();//執(zhí)行的是代理的方法
    }
}

1.2.動(dòng)態(tài)代理
動(dòng)態(tài)代理有以下特點(diǎn):
1.代理對(duì)象,不需要實(shí)現(xiàn)接口
2.代理對(duì)象的生成,是利用JDK的API,動(dòng)態(tài)的在內(nèi)存中構(gòu)建代理對(duì)象(需要我們指定創(chuàng)建代理對(duì)象/目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型)
3.動(dòng)態(tài)代理也叫做:JDK代理,接口代理

接口類IUserDao.java以及接口實(shí)現(xiàn)類,目標(biāo)對(duì)象UserDao是一樣的,沒有做修改.在這個(gè)基礎(chǔ)上,增加一個(gè)代理工廠類(ProxyFactory.java),將代理類寫在這個(gè)地方,然后在測(cè)試類(需要使用到代理的代碼)中先建立目標(biāo)對(duì)象和代理對(duì)象的聯(lián)系,然后代用代理對(duì)象的中同名方法

/**
 * 創(chuàng)建動(dòng)態(tài)代理對(duì)象
 * 動(dòng)態(tài)代理不需要實(shí)現(xiàn)接口,但是需要指定接口類型
 */
public class ProxyFactory{

    //維護(hù)一個(gè)目標(biāo)對(duì)象
    private Object target;
    public ProxyFactory(Object target){
        this.target=target;
    }

   //給目標(biāo)對(duì)象生成代理對(duì)象
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("開始事務(wù)2");
                        //執(zhí)行目標(biāo)對(duì)象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事務(wù)2");
                        return returnValue;
                    }
                }
        );
    }

}
注意該方法是在Proxy類中是靜態(tài)方法,且接收的三個(gè)參數(shù)依次為:

ClassLoader loader,:指定當(dāng)前目標(biāo)對(duì)象使用類加載器,獲取加載器的方法是固定的
Class<?>[] interfaces,:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型
InvocationHandler h:事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入

/**
 * 測(cè)試類
 */
public class App {
    public static void main(String[] args) {
        // 目標(biāo)對(duì)象
        IUserDao target = new UserDao();
        // 【原始的類型 class cn.itcast.b_dynamic.UserDao】
        System.out.println(target.getClass());

        // 給目標(biāo)對(duì)象册烈,創(chuàng)建代理對(duì)象
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        // class $Proxy0   內(nèi)存中動(dòng)態(tài)生成的代理對(duì)象
        System.out.println(proxy.getClass());

        // 執(zhí)行方法   【代理對(duì)象】
        proxy.save();
    }
}

總結(jié):
代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理

1.3.Cglib代理
上面的靜態(tài)代理和動(dòng)態(tài)代理模式都是要求目標(biāo)對(duì)象是實(shí)現(xiàn)一個(gè)接口的目標(biāo)對(duì)象,但是有時(shí)候目標(biāo)對(duì)象只是一個(gè)單獨(dú)的對(duì)象,并沒有實(shí)現(xiàn)任何的接口,這個(gè)時(shí)候就可以使用以目標(biāo)對(duì)象子類的方式類實(shí)現(xiàn)代理,這種方法就叫做:Cglib代理

Cglib代理,也叫作子類代理,它是在內(nèi)存中構(gòu)建一個(gè)子類對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展.

JDK的動(dòng)態(tài)代理有一個(gè)限制,就是使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口,如果想代理沒有實(shí)現(xiàn)接口的類,就可以使用Cglib實(shí)現(xiàn).
Cglib是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展java類與實(shí)現(xiàn)java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
Cglib包的底層是通過使用一個(gè)小而塊的字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類.不鼓勵(lì)直接使用ASM,因?yàn)樗竽惚仨殞?duì)JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉.
Cglib子類代理實(shí)現(xiàn)方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已經(jīng)包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在內(nèi)存中動(dòng)態(tài)構(gòu)建子類
3.代理的類不能為final,否則報(bào)錯(cuò)
4.目標(biāo)對(duì)象的方法如果為final/static,那么就不會(huì)被攔截,即不會(huì)執(zhí)行目標(biāo)對(duì)象額外的業(yè)務(wù)方法.

/**
 * 目標(biāo)對(duì)象,沒有實(shí)現(xiàn)任何接口
 */
public class UserDao {

    public void save() {
        System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
    }
}

/**
 * Cglib子類代理工廠
 * 對(duì)UserDao在內(nèi)存中動(dòng)態(tài)構(gòu)建一個(gè)子類對(duì)象
 */
public class ProxyFactory implements MethodInterceptor{
    //維護(hù)目標(biāo)對(duì)象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //給目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象
    public Object getProxyInstance(){
        //1.工具類
        Enhancer en = new Enhancer();
        //2.設(shè)置父類
        en.setSuperclass(target.getClass());
        //3.設(shè)置回調(diào)函數(shù)
        en.setCallback(this);
        //4.創(chuàng)建子類(代理對(duì)象)
        return en.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("開始事務(wù)...");

        //執(zhí)行目標(biāo)對(duì)象的方法
        Object returnValue = method.invoke(target, args);

        System.out.println("提交事務(wù)...");

        return returnValue;
    }
}

在Spring的AOP編程中:
如果加入容器的目標(biāo)對(duì)象有實(shí)現(xiàn)接口,用JDK代理
如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)接口,用Cglib代理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戈泼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赏僧,更是在濱河造成了極大的恐慌大猛,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淀零,死亡現(xiàn)場(chǎng)離奇詭異挽绩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)驾中,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門唉堪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肩民,你說我怎么就攤上這事唠亚。” “怎么了持痰?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵趾撵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)占调,這世上最難降的妖魔是什么暂题? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮究珊,結(jié)果婚禮上薪者,老公的妹妹穿的比我還像新娘。我一直安慰自己剿涮,他們只是感情好言津,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著取试,像睡著了一般悬槽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞬浓,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天初婆,我揣著相機(jī)與錄音,去河邊找鬼猿棉。 笑死磅叛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萨赁。 我是一名探鬼主播弊琴,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼杖爽!你這毒婦竟也來了敲董?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤慰安,失蹤者是張志新(化名)和其女友劉穎腋寨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泻帮,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年计寇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锣杂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡番宁,死狀恐怖元莫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝶押,我是刑警寧澤踱蠢,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響茎截,放射性物質(zhì)發(fā)生泄漏苇侵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一企锌、第九天 我趴在偏房一處隱蔽的房頂上張望榆浓。 院中可真熱鬧,春花似錦撕攒、人聲如沸陡鹃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萍鲸。三九已至,卻和暖如春擦俐,著一層夾襖步出監(jiān)牢的瞬間脊阴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工捌肴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹬叭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓状知,卻偏偏與公主長(zhǎng)得像秽五,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饥悴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理坦喘,服務(wù)發(fā)現(xiàn),斷路器西设,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 1.1 spring IoC容器和beans的簡(jiǎn)介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器瓣铣,...
    simoscode閱讀 6,717評(píng)論 2 22
  • 一> 代理模式 概述 代理(Proxy)是一種設(shè)計(jì)模式, 提供了對(duì)目標(biāo)對(duì)象另外的訪問方式贷揽;即通過代理訪問目標(biāo)對(duì)象棠笑。...
    奮斗的老王閱讀 1,117評(píng)論 0 50
  • 什么是Spring Spring是一個(gè)開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,464評(píng)論 1 133
  • 一禽绪、試卷分析 本次的語文試卷共十一道大題蓖救,整個(gè)試題基本體現(xiàn)了:注重基礎(chǔ),突出能力印屁,尊重個(gè)性循捺,鼓勵(lì)創(chuàng)新,聯(lián)系生活的原...
    愛呢喃燕子閱讀 284評(píng)論 0 1