<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來說,如何自己實(shí)現(xiàn)Spring AOP?

          共 6237字,需瀏覽 13分鐘

           ·

          2019-08-03 07:00

          640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=1

          正好遇到了一道這樣的題:拋開Spring來說,如何自己實(shí)現(xiàn)Spring AOP?

          就喜歡這樣的題,能把那些天天寫增刪改查從來不思考的人給PK下去,今天就和大家一切學(xué)習(xí)代理模式與Spring AOP。

          代理與裝飾器

          場(chǎng)景描述

          代理,即替代之意,可替代所有功能,即和原類實(shí)現(xiàn)相同的規(guī)范。

          代理模式和裝飾器模式很像,之前的裝飾器講的不是很好,這里換個(gè)例子再講一遍。

          寧靜的午后,來到咖啡館,想喝一杯咖啡。

          基礎(chǔ)實(shí)現(xiàn)

          給你一個(gè)咖啡接口:

          public
          ?
          interface
          ?
          Coffee
          ?
          {
          
          ????
          /**
          ?????*?打印當(dāng)前咖啡的原材料,即咖啡里有什么
          ?????*/
          ????
          void
          ?printMaterial
          ();
          }
          

          一個(gè)默認(rèn)的苦咖啡的實(shí)現(xiàn):

          public
          ?
          class
          ?
          BitterCoffee
          ?
          implements
          ?
          Coffee
          ?
          {
          
          ????
          @Override
          ????
          public
          ?
          void
          ?printMaterial
          ()
          ?
          {
          ????????
          System
          .
          out
          .
          println
          (
          "咖啡"
          );
          ????
          }
          }
          

          默認(rèn)的點(diǎn)餐邏輯:

          public
          ?
          class
          ?
          Main
          ?
          {
          
          ????
          public
          ?
          static
          ?
          void
          ?main
          (
          String
          []
          ?args
          )
          ?
          {
          ????????
          Coffee
          ?coffee?
          =
          ?
          new
          ?
          BitterCoffee
          ();
          ????????coffee
          .
          printMaterial
          ();
          ????
          }
          }
          

          點(diǎn)一杯咖啡。

          7X7DS2LqNA.png

          裝飾器模式

          優(yōu)雅的服務(wù)生把咖啡端了上來,抿了一口,有些苦。

          想加點(diǎn)糖,對(duì)服務(wù)生說:“您好,請(qǐng)為我的咖啡加些糖”。

          /**
          ?*?糖裝飾器,用來給咖啡加糖
          ?*/
          public
          ?
          class
          ?
          SugarDecorator
          ?
          implements
          ?
          Coffee
          ?
          {
          
          ????
          /**
          ?????*?持有的咖啡對(duì)象
          ?????*/
          ????
          private
          ?
          final
          ?
          Coffee
          ?coffee
          ;
          
          ????
          public
          ?
          SugarDecorator
          (
          Coffee
          ?coffee
          )
          ?
          {
          ????????
          this
          .
          coffee?
          =
          ?coffee
          ;
          ????
          }
          
          ????
          @Override
          ????
          public
          ?
          void
          ?printMaterial
          ()
          ?
          {
          ????????
          System
          .
          out
          .
          println
          (
          "糖"
          );
          ????????
          this
          .
          coffee
          .
          printMaterial
          ();
          ????
          }
          }
          

          然后服務(wù)生就拿走了我的咖啡,去使用SugarDecorator為咖啡加糖,最后把加好糖的咖啡給我。

          public
          ?
          class
          ?
          Main
          ?
          {
          
          ????
          public
          ?
          static
          ?
          void
          ?main
          (
          String
          []
          ?args
          )
          ?
          {
          ????????
          Coffee
          ?coffee?
          =
          ?
          new
          ?
          BitterCoffee
          ();
          ????????coffee?
          =
          ?
          new
          ?
          SugarDecorator
          (
          coffee
          );
          ????????coffee
          .
          printMaterial
          ();
          ????
          }
          }
          

          看一看咖啡的成分,對(duì)的,確實(shí)加上了糖!

          2POI4TMu9g.png

          注意看這兩行:

          Coffee
          ?coffee?
          =
          ?
          new
          ?
          BitterCoffee
          ();
          ????????
          //?點(diǎn)了一杯苦咖啡
          coffee?
          =
          ?
          new
          ?
          SugarDecorator
          (
          coffee
          );
          ???????
          //?給咖啡加了點(diǎn)糖
          

          裝飾器模式適合什么場(chǎng)景,我有一個(gè)對(duì)象,但是這個(gè)對(duì)象的功能不能令我滿意,我就拿裝飾器給他裝飾一下。

          代理模式

          周末了,又抱著iPad來到了咖啡館,準(zhǔn)備享受一個(gè)寧靜的下午。

          “先生,請(qǐng)問您要喝點(diǎn)什么?”一旁禮貌的服務(wù)生上前問道。

          上次點(diǎn)的咖啡太苦了,這次直接要個(gè)加糖的吧。

          “我要一杯加糖咖啡。”

          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
          ();
          ????
          }
          }
          

          這是加糖咖啡,其實(shí)內(nèi)部仍然是咖啡,只是加了些配方,就產(chǎn)生了一種新類,一種新的可以在菜單上呈現(xiàn)的飲品。

          點(diǎn)咖啡:

          public
          ?
          class
          ?
          Main
          ?
          {
          
          ????
          public
          ?
          static
          ?
          void
          ?main
          (
          String
          []
          ?args
          )
          ?
          {
          ????????
          Coffee
          ?coffee?
          =
          ?
          new
          ?
          CoffeeWithSugar
          ();
          ????????coffee
          .
          printMaterial
          ();
          ????
          }
          }
          

          正合我意,在咖啡的陪伴下度過了一個(gè)美好的下午。

          tbXHJM6g7S.png

          差別

          故事講完了,兩者實(shí)現(xiàn)的都是對(duì)原對(duì)象的包裝,持有原對(duì)象的實(shí)例,差別在于對(duì)外的表現(xiàn)。

          裝飾器模式:點(diǎn)了咖啡,發(fā)現(xiàn)太苦了,不是自己想要的,然后用裝飾器加了點(diǎn)糖。

          Coffee
          ?coffee?
          =
          ?
          new
          ?
          BitterCoffee
          ();
          coffee?
          =
          ?
          new
          ?
          SugarDecorator
          (
          coffee
          );
          

          代理模式:直接就點(diǎn)的加糖咖啡。

          Coffee
          ?coffee?
          =
          ?
          new
          ?
          CoffeeWithSugar
          ();
          

          很細(xì)微的差別,希望大家不要弄混。

          批評(píng)

          去看代理模式相關(guān)的資料,五花八門,怎么理解的都有。

          我覺得菜鳥教程的代理模式解釋的最為正宗:在代理模式中,我們創(chuàng)建具有現(xiàn)有對(duì)象的對(duì)象,以便向外界提供功能接口。

          hrRNlbN52Y.png

          還有,網(wǎng)上許多設(shè)計(jì)模式的文章都是你抄我、我抄你,一個(gè)錯(cuò)了,全都錯(cuò)了。

          我覺得我需要糾正一下。誰說代理模式一定要用接口的啊?代理模式時(shí)設(shè)計(jì)模式,設(shè)計(jì)模式不分語言,假如一門語言中沒有接口,那它就不能代理模式了嗎?只是Java中的接口可以讓我們符合依賴倒置原則進(jìn)行開發(fā),降低耦合。用抽象類可以嗎?可以。用類繼承可以嗎?也可以。

          思想明白了,用什么寫還不是像玩一樣?

          AOP

          設(shè)計(jì)模式是思想,所以我上面說的代理模式不是僅適用于接口便與Spring AOP息息相關(guān)。

          AOP:Aspect Oriented Programming,面向切面編程,是面向?qū)ο缶幊痰难a(bǔ)充。如果你不明白這句話,好好去學(xué)學(xué)面向?qū)ο缶椭罏槭裁戳恕?/p>

          我們會(huì)聲明切面,即切在某方法之前、之后或前后都執(zhí)行。而Spring AOP的實(shí)現(xiàn)就是代理模式。

          場(chǎng)景

          正好最近寫過短信驗(yàn)證碼,就拿這個(gè)來當(dāng)例子吧。

          public
          ?
          interface
          ?
          SMSService
          ?
          {
          
          ????
          void
          ?sendMessage
          ();
          }
          public
          ?
          class
          ?
          SMSServiceImpl
          ?
          implements
          ?
          SMSService
          ?
          {
          
          ????
          @Override
          ????
          public
          ?
          void
          ?sendMessage
          ()
          ?
          {
          ????????
          System
          .
          out
          .
          println
          (
          "【夢(mèng)云智】您正在執(zhí)行重置密碼操作,您的驗(yàn)證碼為:1234,5分鐘內(nèi)有效,請(qǐng)不要將驗(yàn)證碼轉(zhuǎn)發(fā)給他人。"
          );
          ????
          }
          }
          

          主函數(shù):

          public
          ?
          class
          ?
          Main
          ?
          {
          
          ????
          public
          ?
          static
          ?
          void
          ?main
          (
          String
          []
          ?args
          )
          ?
          {
          ????????
          SMSService
          ?smsService?
          =
          ?
          new
          ?
          SMSServiceImpl
          ();
          ????????smsService
          .
          sendMessage
          ();
          ????????smsService
          .
          sendMessage
          ();
          ????
          }
          }
          

          費(fèi)用統(tǒng)計(jì)

          老板改需求了,發(fā)驗(yàn)證碼要花錢,老板想看看一共在短信上花了多少錢。

          正常按Spring的思路,肯定是聲明一個(gè)切面,來切發(fā)短信的方法,然后在切面內(nèi)統(tǒng)計(jì)短信費(fèi)用。

          只是現(xiàn)在沒有框架,也就是這道題:拋開Spring來說,如何自己實(shí)現(xiàn)Spring AOP?

          寫框架考慮的自然多些,我上文講的代理是靜態(tài)代理,編譯期間就決定好的。而框架實(shí)現(xiàn)卻是動(dòng)態(tài)代理,需要在運(yùn)行時(shí)生成代理對(duì)象,因?yàn)樾枰M(jìn)行類掃描,看看哪些個(gè)類有切面需要生成代理對(duì)象。

          JDK動(dòng)態(tài)代理

          編寫一個(gè)統(tǒng)計(jì)短信費(fèi)用的類實(shí)現(xiàn)InvocationHandler接口。

          寫到這,終于明白為什么每次后臺(tái)Debug的時(shí)候都會(huì)跳轉(zhuǎn)到invoke方法。

          public
          ?
          class
          ?
          MoneyCountInvocationHandler
          ?
          implements
          ?
          InvocationHandler
          ?
          {
          
          ????
          /**
          ?????*?被代理的目標(biāo)
          ?????*/
          ????
          private
          ?
          final
          ?
          Object
          ?target
          ;
          
          ????
          /**
          ?????*?內(nèi)部存儲(chǔ)錢的總數(shù)
          ?????*/
          ????
          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
          ;
          ????
          }
          }
          

          將主函數(shù)里的smsService替換為使用MoneyCountInvocationHandler處理的代理對(duì)象。

          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
          ();
          ????
          }
          }
          

          G7C14AI442.png

          根據(jù)InvocationHandler中的invoke方法動(dòng)態(tài)生成一個(gè)類,該類實(shí)現(xiàn)SMSService接口,代理對(duì)象,就是用這個(gè)類實(shí)例化的。

          eEXIbCxc8E.png

          AOP實(shí)現(xiàn)

          上面的都實(shí)現(xiàn)了?寫一個(gè)AOP是不是也不是難事?

          主函數(shù)的代碼,應(yīng)該放在IOC容器初始化中,掃描包,去看看哪些個(gè)類需要生成代理對(duì)象,然后構(gòu)造代理對(duì)象到容器中。

          然后在invoke方法里,把統(tǒng)計(jì)費(fèi)用的邏輯改成切面的邏輯不就好了嗎?

          不足分析

          結(jié)束了嗎?當(dāng)然沒有,上面的方法實(shí)現(xiàn)僅對(duì)接口有效。

          因?yàn)镴DK的動(dòng)態(tài)代理,是生成一個(gè)實(shí)現(xiàn)相應(yīng)接口的代理類。但是Spring又不是只能通過接口注入。

          @Autowired
          private
          ?
          Type
          ?xxxxx
          ;
          

          Spring的@Autowired是通過聲明的類型去容器里找符合的對(duì)象然后注進(jìn)來的,接口是類型,類不也是類型嗎?

          @Autowired
          private
          ?
          SMSService
          ?smsService
          ;
          

          這樣能注進(jìn)來。

          @Autowired
          private
          ?
          SMSServiceImpl
          ?smsService
          ;
          

          這樣呢?也能注進(jìn)來。

          所以,JDK動(dòng)態(tài)代理針對(duì)直接注入類類型的,就代理不了。

          cglib動(dòng)態(tài)代理

          自古以來,從來都是時(shí)勢(shì)造英雄,而不是英雄創(chuàng)造了時(shí)代。

          出現(xiàn)了問題,自然會(huì)有英雄出來解決。拯救世界的就是cglib。

          JDK動(dòng)態(tài)代理解決不了的,統(tǒng)統(tǒng)交給cglib。

          就這個(gè)來說:

          @Autowired
          private
          ?
          SMSServiceImpl
          ?smsService
          ;
          


          不是使用接口注入的,JDK動(dòng)態(tài)代理解決不了。cglib怎么解決的呢?它會(huì)根據(jù)當(dāng)前的類,動(dòng)態(tài)生成一個(gè)子類,在子類中織入切面邏輯。

          然后使用子類對(duì)象代理父類對(duì)象。這就是為什么我上面說:代理模式,不要拘泥于接口。

          所以織入成功的,都是子類能把父類覆蓋的方法。

          所以cglib也不是萬能的,方法是final的,子類重寫不了,它當(dāng)然也無計(jì)可施了。

          總結(jié)

          讀書讀的是什么?是真正理解作者的思想,明白作者想歌頌什么、批判什么。

          框架學(xué)的是什么?不只是為了提高開發(fā)效率,而是在使用的時(shí)候,就像與設(shè)計(jì)者交流一樣,能真正明白框架設(shè)計(jì)者的思想,才算用明白一款框架。

          如果我們都能做到這般,又何愁設(shè)計(jì)不出一款真正屬于自己的框架呢?


          瀏覽 81
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  青青草成人视频在线观看 | 亚洲无玛在线 | 午夜操逼视频网 | 亚洲综合一区二区 | 蜜桃四季春秘 一区二区三区 |