前言
面向切面編程(思想)AOP Aspect Oriented Programming,是面向對象基礎上更關注最終目標,而不關注中間的小目標,簡而言之,就是我們的目標(例如constroller)觸發了我們關注的方法,此時就執行我們的觀察者行為,例如在目標的方法觸發前做事,觸發后做事(如:編碼處理,登錄認證等等)。
為了讓大家更好的理解,這里把面向過程、面向對象先講解一遍。
面向過程編程(思想)POP
Procedure Oriented Programming,面向過程編程思想(關注事情步驟,事情本身),將一件事情的整體看作是一個流程,我們更關注事情的流程、步驟。比如我去超市買菜,我要下樓,等紅綠燈,走過幾條街道,進入超市選擇自己要的菜,然后排隊買單。我會關注這些過程要經歷的事件,步驟。
面向對象編程(思想)OOP
Object Oriented Programming,面向對象編程思想(關注中間有幾個對象參與其中),將原有整體事情的步驟,拆分成小段,每一個小段封裝成一個單獨的事情(方法),不同的方法交給不同的人來做,例如我去超市買菜,我在家里出發,最終目的是在超市買菜,中間幾個環節交給不同的人(對象)來幫我做:下樓(被人背下去),打車,選菜,排隊等等都有人幫我做,我只要把這些順序連起來,指揮不同的對象按照我要求的順序執行便可。
面向切面編程(思想)AOP
Aspect Oriented Programming,面向切面編程思想(面向對象基礎上 更關注最終目標 而不關注中間的小目標),但要注意切面本身也是對象,比如我去超市買菜,我為起始對象,超市為目標對象,在我到超市之前所經歷的事情都歸納到切面對象(下樓,打車,選菜,排隊執行這些方法的人(對象)等等),連接點(管理切面對象中的方法按順序執行),代理對象負責管理這些切面對象,切點為買菜(即在目標對象中觸發我們關心的方法);
案例
這里就用js模擬了,js模擬簡單些,java模擬的話思路也是一樣的,而且java的spring本身用xml或注解就可以了,這里主要講思想
// 面向對象的方式 let money = 100; class PersonA{ static before(){ console.log("走路去超市買菜"); } } class My{ static buy(){ money -= 55; console.log("買完菜,減去55元"); } } class PersonC{ static after(){ console.log("走路回家"); } } PersonA.before();//走路去超市買菜 My.buy();//買完菜,減去55元 PersonC.after();//走路回家
// 面向切面的方式 class agency{//可以理解為過濾器 before(){ console.log("走路去超市"); } buy(Func,spend){//代理對象 this.before(); Func(spend); this.after(); } after(){ console.log("走路回家"); } } let agencyObj = new agency();//找代理,類似找人幫我們買菜送上門,這方式好處在於其他人還可以找代理買家具,買學習用品等等,我們生成了一次代理,然后傳不同的任務進去讓代理幫我們執行 class Myself{ buy(agency,spend){ agencyObj.buy(agency,spend);//代理對象幫我們執行的買菜方法 } } let my = new Myself(10000);//實例化自己 //制定所要做的第一件事 function buy(spend) { money -= spend; console.log("買完菜,減去"+spend+"元,還剩:"+money+"元"); } my.buy(buy,55);//這里我們以為還是自己調用方法去買菜,實際底層是代理對象幫我們執行的買菜方法 money = 1000; //制定所要做的第二件事 function buy2(spend) { money -= spend; console.log("買完日用品,減去"+spend+"元,還剩:"+money+"元"); } my.buy(buy2,198); //可以看到,每次我們只需要把最終要實現的功能交給代理,最終代理對象返回想要的結果給我們 // 執行結果: // 走路去超市 // 買完菜,減去55元,還剩:45元 // 走路回家 // 走路去超市 // 買完日用品,減去198元,還剩:802元 // 走路回家
上面的before和after方法可以當成過濾器,比如我要得到的數據渲染前先解密,解密渲染后,部分數據用*替換這類場景。
稍微復雜一點的使用方式
設置前置方法--before,后置方法--after-returning,異常處理--after-throwing,最終執行--after,環繞--around;
前置方法是在目標方法前執行,后置方法在目標方法之后執行,異常處理即處理異常事件,最終執行即不管發生什么都一定會觸發的事件,環繞即在目標方法觸發時先后執行的事件,類似前置方法加后置方法。
案例:
// 面向切面的方式 class agency{//可以理解為過濾器 beforeMethod(){ console.log("走路去超市"); } buy(Func,spend){//代理對象 try{ // 前置 this.beforeMethod(); // 環繞前 // 目標方法(環繞的是目標的前后) // Func(spend); this.aroundMethod(Func,spend); // 環繞后 // 后置 this.afterReturningMethod(); }catch(e){ // 異常 this.afterThrowingMethod(); }finally{ // 最終 this.afterMethod(); } } afterReturningMethod(){ console.log("走路回家"); } afterThrowingMethod(){ console.log("代購有事,此單取消"); } afterMethod(){ console.log("謝謝您,辛苦啦"); } aroundMethod(Func,spend){ console.log("上電梯"); Func(spend); console.log("下電梯"); } } let agencyObj = new agency();//找代理,類似找人幫我們買菜送上門,這方式好處在於其他人還可以找代理買家具,買學習用品等等,我們生成了一次代理,然后傳不同的任務進去讓代理幫我們執行 class Myself{ buy(agency,spend){ agencyObj.buy(agency,spend);//代理對象幫我們執行的買菜方法 } } let my = new Myself(10000);//實例化自己 //制定所要做的第一件事 function buy(spend) { money -= spend; console.log("買完菜,減去"+spend+"元,還剩:"+money+"元"); } my.buy(buy,55);//這里我們以為還是自己調用方法去買菜,實際底層是代理對象幫我們執行的買菜方法 money = 1000; //制定所要做的第二件事 function buy2(spend) { money -= spend; console.log("買完日用品,減去"+spend+"元,還剩:"+money+"元"); } my.buy(buy2,198); //可以看到,每次我們只需要把最終要實現的功能交給代理,最終代理對象返回想要的結果給我們 // 執行結果: // 走路去超市 // 上電梯 // 買完菜,減去55元,還剩:45元 // 下電梯 // 走路回家 // 謝謝您,辛苦啦 // 走路去超市 // 上電梯 // 買完日用品,減去198元,還剩:802元 // 下電梯 // 走路回家 // 謝謝您,辛苦啦
這里也可以設置為前置方法和后置方法為默認,其他方法體根據我們傳入的參數值的不同(例如數字)來判斷執行哪些方法,就是約定優於配置,大家約定俗成,會更有效率。
思路
面向切面的方式就是更注重結果而不注重中間實現的步驟,中間無論由多少個切面對象幫你做了事,都由一個代理對象來幫你管理執行,而我們需要做的就是把要做的事告訴代理對象。當然面向切面的方式是否使用要根據業務場景來定,如果發現有多個方法中間需要執行的步驟流程一致,而這些方法只需要拿到經過這些步驟之后所得到的結果,不關心中間發生了什么,就使用面向切面的方式。