<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何自己實現 Spring AOP?

          共 5694字,需瀏覽 12分鐘

           ·

          2022-02-22 09:57

          以下內容來自公眾號逆鋒起筆,關注每日干貨及時送達

          轉自:張喜碩

          鏈接:https://segmentfault.com/a/1190000019148468

          正好遇到了一道這樣的題:拋開Spring來說,如何自己實現Spring AOP?就喜歡這樣的題,能把那些天天寫增刪改查從來不思考的人給PK下去,今天就和大家一切學習代理模式與Spring AOP

          | 代理與裝飾器

          場景描述

          代理,即替代之意,可替代所有功能,即和原類實現相同的規(guī)范。
          代理模式和裝飾器模式很像,之前的裝飾器講的不是很好,這里換個例子再講一遍。
          寧靜的午后,來到咖啡館,想喝一杯咖啡。

          基礎實現

          給你一個咖啡接口:
          public?interface?Coffee?{ /** * 打印當前咖啡的原材料,即咖啡里有什么 */ void printMaterial();}
          一個默認的苦咖啡的實現:
          public class BitterCoffee implements Coffee {
          @Override public void printMaterial() { System.out.println("咖啡"); }}
          默認的點餐邏輯:
          public class Main {
          public static void main(String[] args) { Coffee coffee = new BitterCoffee(); coffee.printMaterial(); }}
          點一杯咖啡。

          裝飾器模式

          優(yōu)雅的服務生把咖啡端了上來,抿了一口,有些苦。想加點糖,對服務生說:“您好,請為我的咖啡加些糖”。
          /** * 糖裝飾器,用來給咖啡加糖 */public class SugarDecorator implements Coffee {
          /** * 持有的咖啡對象 */ private final Coffee coffee;
          public SugarDecorator(Coffee coffee) { this.coffee = coffee; }
          @Override public void printMaterial() { System.out.println("糖"); this.coffee.printMaterial(); }}
          然后服務生就拿走了我的咖啡,去使用SugarDecorator為咖啡加糖,最后把加好糖的咖啡給我。
          public class Main {
          public static void main(String[] args) { Coffee coffee = new BitterCoffee(); coffee = new SugarDecorator(coffee); coffee.printMaterial(); }}
          看一看咖啡的成分,對的,確實加上了糖!
          注意看這兩行:
          Coffee coffee = new BitterCoffee(); // 點了一杯苦咖啡coffee?=?new?SugarDecorator(coffee);???????//?給咖啡加了點糖
          裝飾器模式適合什么場景,我有一個對象,但是這個對象的功能不能令我滿意,我就拿裝飾器給他裝飾一下。

          代理模式

          周末了,又抱著iPad來到了咖啡館,準備享受一個寧靜的下午。
          “先生,請問您要喝點什么?”一旁禮貌的服務生上前問道。
          上次點的咖啡太苦了,這次直接要個加糖的吧。
          “我要一杯加糖咖啡?!?/span>
          public class CoffeeWithSugar implements Coffee {
          private final Coffee coffee;
          public CoffeeWithSugar() { this.coffee = new BitterCoffee(); }
          @Override public void printMaterial() { System.out.println("糖"); this.coffee.printMaterial(); }}
          這是加糖咖啡,其實內部仍然是咖啡,只是加了些配方,就產生了一種新類,一種新的可以在菜單上呈現的飲品。
          點咖啡:
          public class Main {
          public static void main(String[] args) { Coffee coffee = new CoffeeWithSugar(); coffee.printMaterial(); }}
          正合我意,在咖啡的陪伴下度過了一個美好的下午。

          差別

          故事講完了,兩者實現的都是對原對象的包裝,持有原對象的實例,差別在于對外的表現。
          裝飾器模式:點了咖啡,發(fā)現太苦了,不是自己想要的,然后用裝飾器加了點糖。
          Coffee coffee = new BitterCoffee();coffee = new SugarDecorator(coffee);
          代理模式:直接就點的加糖咖啡。
          Coffee coffee = new CoffeeWithSugar();
          很細微的差別,希望大家不要弄混。

          批評

          去看代理模式相關的資料,五花八門,怎么理解的都有。
          我覺得菜鳥教程的代理模式解釋的最為正宗:在代理模式中,我們創(chuàng)建具有現有對象的對象,以便向外界提供功能接口。微信搜索readdot,關注后回復視頻教程獲取23種精品資料
          還有,網上許多設計模式的文章都是你抄我、我抄你,一個錯了,全都錯了。
          我覺得我需要糾正一下。誰說代理模式一定要用接口的???代理模式是設計模式,設計模式不分語言,假如一門語言中沒有接口,那它就不能代理模式了嗎?只是Java中的接口可以讓我們符合依賴倒置原則進行開發(fā),降低耦合。用抽象類可以嗎?可以。用類繼承可以嗎?也可以。
          思想明白了,用什么寫還不是像玩一樣?

          | AOP

          設計模式是思想,所以我上面說的代理模式不是僅適用于接口便與Spring AOP息息相關。
          AOPAspect Oriented Programming,面向切面編程,是面向對象編程的補充。如果你不明白這句話,好好去學學面向對象就知道為什么了。
          我們會聲明切面,即切在某方法之前、之后或前后都執(zhí)行。而Spring AOP的實現就是代理模式。

          場景

          正好最近寫過短信驗證碼,就拿這個來當例子吧。
          public interface SMSService {
          void sendMessage();}public class SMSServiceImpl implements SMSService {
          @Override public void sendMessage() { System.out.println("【夢云智】您正在執(zhí)行重置密碼操作,您的驗證碼為:1234,5分鐘內有效,請不要將驗證碼轉發(fā)給他人。"); }}
          主函數:
          public class Main {
          public static void main(String[] args) { SMSService smsService = new SMSServiceImpl(); smsService.sendMessage(); smsService.sendMessage(); }}

          費用統計

          老板改需求了,發(fā)驗證碼要花錢,老板想看看一共在短信上花了多少錢。
          正常按Spring的思路,肯定是聲明一個切面,來切發(fā)短信的方法,然后在切面內統計短信費用。
          只是現在沒有框架,也就是這道題:拋開Spring來說,如何自己實現Spring AOP?
          寫框架考慮的自然多些,我上文講的代理是靜態(tài)代理,編譯期間就決定好的。而框架實現卻是動態(tài)代理,需要在運行時生成代理對象,因為需要進行類掃描,看看哪些個類有切面需要生成代理對象。

          JDK動態(tài)代理

          編寫一個統計短信費用的類實現InvocationHandler接口。
          寫到這,終于明白為什么每次后臺Debug的時候都會跳轉到invoke方法。
          public class MoneyCountInvocationHandler implements InvocationHandler {
          /** * 被代理的目標 */ private final Object target;
          /** * 內部存儲錢的總數 */ private Double moneyCount;
          public MoneyCountInvocationHandler(Object target) { this.target = target; this.moneyCount = 0.0; }
          @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(target, args); moneyCount += 0.07; System.out.println("發(fā)送短信成功,共花了:" + moneyCount + "元"); return result; }}
          將主函數里的smsService替換為使用MoneyCountInvocationHandler處理的代理對象。
          public class Main {
          public static void main(String[] args) { SMSService smsService = new SMSServiceImpl(); smsService = (SMSService) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{SMSService.class}, new MoneyCountInvocationHandler(smsService)); smsService.sendMessage(); smsService.sendMessage(); }}


          根據InvocationHandler中的invoke方法動態(tài)生成一個類,該類實現SMSService接口,代理對象,就是用這個類實例化的。


          AOP實現

          上面的都實現了?寫一個AOP是不是也不是難事?
          主函數的代碼,應該放在IOC容器初始化中,掃描包,去看看哪些個類需要生成代理對象,然后構造代理對象到容器中。
          然后在invoke方法里,把統計費用的邏輯改成切面的邏輯不就好了嗎?

          不足分析

          結束了嗎?當然沒有,上面的方法實現僅對接口有效。
          因為JDK的動態(tài)代理,是生成一個實現相應接口的代理類。但是Spring又不是只能通過接口注入。
          @Autowiredprivate Type xxxxx;
          Spring@Autowired是通過聲明的類型去容器里找符合的對象然后注進來的,接口是類型,類不也是類型嗎?
          @Autowiredprivate SMSService smsService;
          這樣能注進來。
          @Autowiredprivate?SMSServiceImpl?smsService;
          這樣呢?也能注進來。
          所以,JDK動態(tài)代理針對直接注入類類型的,就代理不了。

          cglib動態(tài)代理

          自古以來,從來都是時勢造英雄,而不是英雄創(chuàng)造了時代。
          出現了問題,自然會有英雄出來解決。拯救世界的就是cglib
          JDK動態(tài)代理解決不了的,統統交給cglib。
          就這個來說:
          @Autowiredprivate SMSServiceImpl smsService;
          不是使用接口注入的,JDK動態(tài)代理解決不了。cglib怎么解決的呢?它會根據當前的類,動態(tài)生成一個子類,在子類中織入切面邏輯。
          然后使用子類對象代理父類對象。這就是為什么我上面說:代理模式,不要拘泥于接口。
          所以織入成功的,都是子類能把父類覆蓋的方法。
          所以cglib也不是萬能的,方法是final的,子類重寫不了,它當然也無計可施了。

          | 總結

          讀書讀的是什么?是真正理解作者的思想,明白作者想歌頌什么、批判什么。
          框架學的是什么?不只是為了提高開發(fā)效率,而是在使用的時候,就像與設計者交流一樣,能真正明白框架設計者的思想,才算用明白一款框架。
          如果我們都能做到這般,又何愁設計不出一款真正屬于自己的框架呢?

          9 個實用 shell 腳本,建議收藏!

          IDEA 官方數據庫管理神器,比 Navicat 還香?(文末贈書)

          圖解 SpringMVC 五大組件

          編寫 Spring MVC 控制器的 14 個技巧!

          SpringBoot 打包部署最佳實踐



          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色男人的天堂 | 影音先锋熟女av 永久免费看黄网址 | 久久久黄色免费视频 | 高清毛片AAAAAAAAA片 | 在线一区二区视频 |