深入理解Spring事務(wù)和Spring AOP

什么是AOP編程

  1. Aspect Oriented Programming 面向切面編程
  2. 主要應(yīng)用場景是日志記錄,權(quán)限控制,性能監(jiān)控,事務(wù)處理,異常處理等
  3. 主要意圖是將日志記錄,權(quán)限控制,性能監(jiān)控,事務(wù)處理,異常處理等代碼從業(yè)務(wù)邏輯代碼中抽取出來,降低耦合性并解決代碼復(fù)用的問題
  4. 底層實現(xiàn)原理是代理設(shè)計模式,可以通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)在不修改源代碼的情況下給程序動態(tài)統(tǒng)一添加功能.假設(shè)把應(yīng)用程序想成一個立體結(jié)構(gòu)的話,OOP的利刃是縱向切入系統(tǒng),把系統(tǒng)劃分為很多個模塊(如: 用戶模塊,文章模塊等等),而AOP的利刃是橫向切入系統(tǒng),提取各個模塊可能都要重復(fù)操作的部分(如: 權(quán)限檢查,日志記錄等等).由此可見,AOP是OOP的一個有效補充

名詞解釋

  1. 關(guān)注點: 重復(fù)代碼(如打印日志的代碼)
  2. 切面/切面類: 關(guān)注點形成的類,將重復(fù)代碼抽取出來,在運行的時候在業(yè)務(wù)方法上動態(tài)植入
  3. 切入點: 執(zhí)行目標對象方法,動態(tài)植入切面代碼(可以通過切入點表達式,指定攔截哪些類的哪些方法;給指定的類在運行的時候植入切面類代碼)

AOP底層實現(xiàn)原理

代理的分類:

靜態(tài)代理和動態(tài)代理

靜態(tài)代理和動態(tài)代理的區(qū)別:

靜態(tài)代理是靜態(tài)定義代理類(需要手寫代理類);動態(tài)代理是動態(tài)生成代理類(虛擬生成代理類)

動態(tài)代理分為:

jdk動態(tài)代理和cglib動態(tài)代理

jdk動態(tài)代理和cglib的區(qū)別:

jdk是基于反射機制實現(xiàn)的;
cglib是基于ASM字節(jié)碼包裝的一個類庫(性能比反射要高一些)(ASM是一個java字節(jié)碼操控框架,它能被用來動態(tài)生成類或者增強既有類的功能.ASM可以直接產(chǎn)生二進制class文件,也可以在類被加載進java虛擬機之前動態(tài)改變類的行為.)

  1. AOP實現(xiàn)原理: 靜態(tài)代理和動態(tài)代理

  2. 什么是靜態(tài)代理: 由程序員創(chuàng)建或工具生成代理類的源碼,再編譯代理類.所謂靜態(tài)也就是在程序運行前就已經(jīng)存在代理類的字節(jié)碼文件,代理類和委托類的關(guān)系在運行前就確定了.

    //接口
    public interface IUserDao {
        void save();
    }
    
    //被代理類
    public class UserDaoImpl implements IUserDao {
    
        @Override
        public void save() {
            System.out.println("假裝把數(shù)據(jù)入庫了...");
        }
    }
    
    //代理類
    public class UserDaoProxy implements IUserDao {
        private IUserDao target;
    
        public UserDaoProxy(IUserDao userDao) {
            this.target = userDao;
        }
    
        @Override
        public void save() {
            System.out.println("開啟事務(wù)...");
            target.save();
            System.out.println("提交事務(wù)...");
        }
    }
    
  3. 什么是動態(tài)代理: 代理類不需要實現(xiàn)接口,動態(tài)的在內(nèi)存中構(gòu)建代理對象(需要我們指定創(chuàng)建代理對象/目標對象實現(xiàn)的接口的類型)

  • jdk動態(tài)代理(原理是根據(jù)類加載器和接口創(chuàng)建代理類,此代理類是接口的實現(xiàn)類,所以必須使用接口,面向接口生成代理,接口位于java.lang.reflect包下)

    public class InvocationHandlerImpl implements InvocationHandler{
    
        // 這其實是業(yè)務(wù)實現(xiàn)類對象芦劣,用來調(diào)用具體的業(yè)務(wù)方法
        private Object target;
    
        // 通過構(gòu)造函數(shù)傳入目標對象
        public InvocationHandlerImpl(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            Object result = null;
            System.out.println("調(diào)用開始處理---");
            result = method.invoke(target, args);
            System.out.println("調(diào)用結(jié)束處理---");
            return result;
    
        }
    
        public static void main(String[] args){
            // 被代理對象
            IUserDao userDao = new UserDaoImpl();
            InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
            ClassLoader loader = userDao.getClass().getClassLoader();
            Class<?>[] interfaces = userDao.getClass().getInterfaces();
            // 主要裝載器虚吟、一組接口及調(diào)用處理動態(tài)代理實例
            IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
            newProxyInstance.save();
    
        }
    
    }
    
  • cglib動態(tài)代理(原理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節(jié)碼生成子類來處理)

    public class CglibProxy implements MethodInterceptor {
    
        private Object targetObject;
        // 這里的目標類型為Object,則可以接受任意一種參數(shù)作為被代理類偏塞,實現(xiàn)了動態(tài)代理
        public Object getInstance(Object target) {
            // 設(shè)置需要創(chuàng)建子類的類
            this.targetObject = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("開啟事務(wù)..");
            Object result = proxy.invoke(targetObject, args);
            System.out.println("關(guān)閉事務(wù)..");
            // 返回代理對象
            return result;
        }
        
        public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy();
            UserDaoImpl userDao = (UserDaoImpl) cglibProxy.getInstance(new UserDaoImpl());
            userDao.save();
        }
    }
    
    
  • jdk動態(tài)代理和cglib動態(tài)代理的區(qū)別

    java動態(tài)代理是利用反射機制生成一個實現(xiàn)代理接口的匿名類灸叼,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。

    而cglib動態(tài)代理是利用asm開源包屁魏,對代理對象類的class文件加載進來捉腥,通過修改其字節(jié)碼生成子類來處理。

    Spring中:

    如果目標對象實現(xiàn)了接口抵碟,默認情況下會采用JDK的動態(tài)代理實現(xiàn)AOP ;
    如果目標對象實現(xiàn)了接口拟逮,可以強制使用CGLIB實現(xiàn)AOP ;
    如果目標對象沒有實現(xiàn)了接口,必須采用CGLIB庫唱歧,spring會自動在JDK動態(tài)代理和CGLIB之間轉(zhuǎn)換 ;
    JDK動態(tài)代理只能對實現(xiàn)了接口的類生成代理颅崩,而不能針對類 ;
    CGLIB是針對類實現(xiàn)代理蕊苗,主要是對指定的類生成一個子類,覆蓋其中的方法 ;
    因為是繼承朽砰,所以該類或方法最好不要聲明成final ,final可以阻止繼承和多態(tài)漆弄。

AOP編程使用

  1. 注解版本實現(xiàn)AOP
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 開啟事物注解權(quán)限

    @Component //交給Spring來處理
    @Aspect    //指定一個類為切面類
    public class AopLog {
        // 前置通知
        @Before("execution(* com.pliu.service.UserService.add(..))")
        public void begin() {
            System.out.println("前置通知");
        }
    
        // 后置通知
        @After("execution(* com.pliu.service.UserService.add(..))")
        public void commit() {
            System.out.println("后置通知");
        }
    
        // 運行通知
        @AfterReturning("execution(* com.pliu.service.UserService.add(..))")
        public void returning() {
            System.out.println("運行通知");
        }
    
        // 異常通知
        @AfterThrowing("execution(* com.pliu.service.UserService.add(..))")
        public void afterThrowing() {
            System.out.println("異常通知");
        }
    
        // 環(huán)繞通知
        @Around("execution(* com.pliu.service.UserService.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("環(huán)繞通知開始");
            proceedingJoinPoint.proceed();
            System.out.println("環(huán)繞通知結(jié)束");
        }
    
    }
    
    
  1. xml版本實現(xiàn)AOP(不好意思,感覺麻煩懶得寫,如有必要還請百度吧)

Spring事務(wù)使用

  1. 事務(wù)基本特性

    ⑴ 原子性(Atomicity)
      原子性是指事務(wù)包含的所有操作要么全部成功撼唾,要么全部失敗回滾哥蔚,因此事務(wù)的操作如果成功就必須要完全應(yīng)用到數(shù)據(jù)庫蛛蒙,如果操作失敗則不能對數(shù)據(jù)庫有任何影響渤愁。

    ⑵ 一致性(Consistency)
     一致性是指事務(wù)必須使數(shù)據(jù)庫從一個一致性狀態(tài)變換到另一個一致性狀態(tài),也就是說一個事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)诺苹。
      拿轉(zhuǎn)賬來說他挎,假設(shè)用戶A和用戶B兩者的錢加起來一共是5000,那么不管A和B之間如何轉(zhuǎn)賬筹淫,轉(zhuǎn)幾次賬呢撞,事務(wù)結(jié)束后兩個用戶的錢相加起來應(yīng)該還得是5000,這就是事務(wù)的一致性摧阅。

    ⑶ 隔離性(Isolation)
      隔離性是當多個用戶并發(fā)訪問數(shù)據(jù)庫時绷蹲,比如操作同一張表時,數(shù)據(jù)庫為每一個用戶開啟的事務(wù)祝钢,不能被其他事務(wù)的操作所干擾,多個并發(fā)事務(wù)之間要相互隔離蜒什。
      即要達到這么一種效果:對于任意兩個并發(fā)的事務(wù)T1和T2疤估,在事務(wù)T1看來,T2要么在T1開始之前就已經(jīng)結(jié)束钞瀑,要么在T1結(jié)束之后才開始慷荔,這樣每個事務(wù)都感覺不到有其他事務(wù)在并發(fā)地執(zhí)行。

    ⑷ 持久性(Durability)
      持久性是指一個事務(wù)一旦被提交了监徘,那么對數(shù)據(jù)庫中的數(shù)據(jù)的改變就是永久性的,即便是在數(shù)據(jù)庫系統(tǒng)遇到故障的情況下也不會丟失提交事務(wù)的操作凰盔。
    例如我們在使用JDBC操作數(shù)據(jù)庫時,在提交事務(wù)方法后落剪,提示用戶事務(wù)操作完成尿庐,當我們程序執(zhí)行完成直到看到提示后,就可以認定事務(wù)以及正確提交凡泣,即使這時候數(shù)據(jù)庫出現(xiàn)了問題皮假,也必須要將我們的事務(wù)完全執(zhí)行完成,否則就會造成我們看到提示事務(wù)處理完畢贺纲,但是數(shù)據(jù)庫因為故障而沒有執(zhí)行事務(wù)的重大錯誤褪测。

  1. 事務(wù)控制分類
  • 編程式事務(wù): 手動事務(wù)(自己去begin,commit,rollback),靈活但繁瑣

  • 聲明式事務(wù): 自動事務(wù),原理是使用編程式事務(wù)+反射機制進行包裝.分注解版本和xml版本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市懈叹,隨后出現(xiàn)的幾起案子萝毛,更是在濱河造成了極大的恐慌滑黔,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庵佣,死亡現(xiàn)場離奇詭異巴粪,居然都是意外死亡,警方通過查閱死者的電腦和手機肛根,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門派哲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人芭届,你說我怎么就攤上這事〕炙恚” “怎么了逃片?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵褥实,是天一觀的道長。 經(jīng)常有香客問我性锭,道長,這世上最難降的妖魔是什么她奥? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任怎棱,我火速辦了婚禮,結(jié)果婚禮上凡资,老公的妹妹穿的比我還像新娘谬运。我一直安慰自己,他們只是感情好伞访,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布轰驳。 她就那樣靜靜地躺著,像睡著了一般冒黑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辛馆,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天豁延,我揣著相機與錄音,去河邊找鬼苔可。 笑死袋狞,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的同蜻。 我是一名探鬼主播早处,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼默责!你這毒婦竟也來了咸包?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤媒熊,失蹤者是張志新(化名)和其女友劉穎坟比,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怜校,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡注竿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年巩割,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宣谈。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡闻丑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嗦嗡,到底是詐尸還是另有隱情,我是刑警寧澤叁执,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布谈宛,位于F島的核電站胎署,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏琼牧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一滋恬、第九天 我趴在偏房一處隱蔽的房頂上張望抱究。 院中可真熱鬧,春花似錦鼓寺、人聲如沸妈候。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纺念,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烙博,已是汗流浹背烟逊。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留图毕,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓眷唉,卻偏偏與公主長得像予颤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子冬阳,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

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