<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 詳解一

          共 8990字,需瀏覽 18分鐘

           ·

          2020-10-20 13:42

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          ? 作者?|??夢想家haima

          來源 |? urlify.cn/7nymqu

          66套java從入門到精通實戰(zhàn)課程分享

          Aop 是一個編程思想,最初是一個理論,最后落地成了很多的技術(shù)實現(xiàn)。

          我們寫一個系統(tǒng),都希望盡量少寫點兒重復的東西。而很多時候呢,又不得不寫一些重復的東西。比如訪問某些方法的權(quán)限執(zhí)行某些方法性能的日志數(shù)據(jù)庫操作的方法進行事務(wù)控制。以上提到的,權(quán)限的控制,事務(wù)控制,性能監(jiān)控的日志 可以叫一個切面。像一個橫切面穿過這一些列需要控制的方法。通過aop編程,實現(xiàn)了對切面業(yè)務(wù)的統(tǒng)一處理。

          以上是我對aop的一個總體概括

          aop的原始實現(xiàn)

          通過動態(tài)代理和反射實現(xiàn),又稱之為JDK動態(tài)代理

          • MyInterceptor.java

          package?demo.aop.jdkproxy;

          import?java.lang.reflect.InvocationHandler;
          import?java.lang.reflect.Method;

          /**
          ?*?攔截器
          ?*????1、目標類導入進來
          ?*????2、事務(wù)導入進來
          ?*????3、invoke完成
          ?*????????1、開啟事務(wù)
          ?*????????2、調(diào)用目標對象的方法
          ?*????????3、事務(wù)的提交
          ?*?@author?zd
          ?*
          ?*/
          public?class?MyInterceptor?implements?InvocationHandler{
          ????private?Object?target;//目標類
          ????private?Transaction?transaction;


          ????public?MyInterceptor(Object?target,?Transaction?transaction)?{
          ????????super();
          ????????this.target?=?target;
          ????????this.transaction?=?transaction;
          ????}


          ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)
          ????????????throws?Throwable?{
          ????????String?methodName?=?method.getName();
          ????????if("savePerson".equals(methodName)||"updatePerson".equals(methodName)
          ????????????????||"deletePerson".equals(methodName)){
          ????????????this.transaction.beginTransaction();//開啟事務(wù)
          ????????????method.invoke(target);//調(diào)用目標方法
          ????????????this.transaction.commit();//事務(wù)的提交
          ????????}else{
          ????????????method.invoke(target);
          ????????}
          ????????return?null;
          ????}
          }
          • PersonDao.java

          package?demo.aop.jdkproxy;

          public?interface?PersonDao?{
          ????public?void?savePerson();
          ????public?void?updatePerson();
          }
          • PersonDaoImpl.java

          package?demo.aop.jdkproxy;

          public?class?PersonDaoImpl?implements?PersonDao{
          ????public?void?savePerson()?{
          ????????System.out.println("save?person");
          ????}

          ????public?void?updatePerson()?{
          ????????//?TODO?Auto-generated?method?stub
          ????????System.out.println("update?person");
          ????}
          }
          • Transaction.java

          package?demo.aop.jdkproxy;

          public?class?Transaction?{
          ????public?void?beginTransaction(){
          ????????System.out.println("begin?transaction");
          ????}
          ????public?void?commit(){
          ????????System.out.println("commit");
          ????}
          }

          • JDKProxyTest

          package?demo.aop.jdkproxy;

          import?org.junit.Test;

          import?java.lang.reflect.Proxy;

          /**
          ?*?1、攔截器的invoke方法是在時候執(zhí)行的?
          ?*?????當在客戶端,代理對象調(diào)用方法的時候,進入到了攔截器的invoke方法
          ?*?2、代理對象的方法體的內(nèi)容是什么?
          ?*?????攔截器的invoke方法的內(nèi)容就是代理對象的方法的內(nèi)容
          ?*?3、攔截器中的invoke方法中的參數(shù)method是誰在什么時候傳遞過來的?
          ?*?????代理對象調(diào)用方法的時候,進入了攔截器中的invoke方法,所以invoke方法中的參數(shù)method就是
          ?*???????代理對象調(diào)用的方法
          ?*?@author?zd
          ?*
          ?*/
          public?class?JDKProxyTest?{
          ????@Test
          ????public?void?testJDKProxy(){
          ????????/**
          ?????????*?1、創(chuàng)建一個目標對象
          ?????????*?2、創(chuàng)建一個事務(wù)
          ?????????*?3、創(chuàng)建一個攔截器
          ?????????*?4、動態(tài)產(chǎn)生一個代理對象
          ?????????*/
          ????????Object?target?=?new?PersonDaoImpl();
          ????????Transaction?transaction?=?new?Transaction();
          ????????MyInterceptor?interceptor?=?new?MyInterceptor(target,?transaction);
          ????????/**
          ?????????*?1、目標類的類加載器
          ?????????*?2、目標類實現(xiàn)的所有的接口
          ?????????*?3、攔截器
          ?????????*/
          ????????PersonDao?personDao?=?(PersonDao)?Proxy.newProxyInstance(target.getClass().getClassLoader(),
          ????????????????target.getClass().getInterfaces(),?interceptor);
          ????????//personDao.savePerson();
          ????????personDao.updatePerson();
          ????}
          }

          運行結(jié)果

          begin?transaction
          update?person
          commit

          原始實現(xiàn)部分,想必現(xiàn)在很少會有人再這么寫了。但這個對于我們理解Aop的思想很有幫助。

          • 我們可以看到 代理對象 personDao調(diào)用的方法updatePerson中沒有模擬事務(wù)的代碼,但最終代理對象卻輸出了begin transactioncommit

          Spring AOP概念核心詞

          • 切面(Aspect):一個關(guān)注點的模塊化,這個關(guān)注點可能會橫切多個對象。事務(wù)管理是J2EE應(yīng)用中一個關(guān)于橫切關(guān)注點的很好的例子。在Spring AOP中,切面可以使用基于模式)或者基于@Aspect注解的方式來實現(xiàn)。

          • 連接點(Joinpoint):在程序執(zhí)行過程中某個特定的點,比如某方法調(diào)用的時候或者處理異常的時候。在Spring AOP中,一個連接點總是表示一個方法的執(zhí)行。

          • 通知(Advice):在切面的某個特定的連接點上執(zhí)行的動作。其中包括了“around”、“before”和“after”等不同類型的通知(通知的類型將在后面部分進行討論)。許多AOP框架(包括Spring)都是以攔截器做通知模型,并維護一個以連接點為中心的攔截器鏈。

          • 切入點(Pointcut):匹配連接點的斷言。通知和一個切入點表達式關(guān)聯(lián),并在滿足這個切入點的連接點上運行(例如,當執(zhí)行某個特定名稱的方法時)。切入點表達式如何和連接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法。

          • 引入(Introduction):用來給一個類型聲明額外的方法或?qū)傩裕ㄒ脖环Q為連接類型聲明(inter-type declaration))。Spring允許引入新的接口(以及一個對應(yīng)的實現(xiàn))到任何被代理的對象。例如,你可以使用引入來使一個bean實現(xiàn)IsModified接口,以便簡化緩存機制。

          • 目標對象(Target Object):被一個或者多個切面所通知的對象。也被稱做被通知(advised)對象。既然Spring AOP是通過運行時代理實現(xiàn)的,這個對象永遠是一個被代理(proxied)對象。

          • AOP代理(AOP Proxy):AOP框架創(chuàng)建的對象,用來實現(xiàn)切面契約(例如通知方法執(zhí)行等等)。在Spring中,AOP代理可以是JDK動態(tài)代理或者CGLIB代理。

          • 織入(Weaving):把切面連接到其它的應(yīng)用程序類型或者對象上,并創(chuàng)建一個被通知的對象。這些可以在編譯時(例如使用AspectJ編譯器),類加載時和運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。

          上面為官方文檔,有的地方還是很難讀懂,畢竟是純概念。下面我用自己的話來翻譯一下,如果有不對的地方,請指正

          • 切面?統(tǒng)一處理的業(yè)務(wù),比如上文提到的 權(quán)限控制,事務(wù)處理

          • 連接點?原本被執(zhí)行的方法,一個執(zhí)行的方法可能被多個切面橫切

          • 通知?切面方法的執(zhí)行,比如權(quán)限控制的具體執(zhí)行過程(權(quán)限控制可以用前置通知@Before)

          • 切入點?切入點的概念通常和連接點概念容易分不清,切入點其實是一個規(guī)則,也就是說什么樣的情況下(滿足什么規(guī)則),
            就會去執(zhí)行鏈接點的那些方法,這個規(guī)則就是切入點,這種規(guī)則用切入點表達式去制定

          • 引入(Introduction)?被代理的對象可以引入新接口,通過默認的實現(xiàn)類,讓這個被代理的類增強

          • 目標對象?就是被切面執(zhí)行了的對象

          • AOP代理?代理包括jdk代理和cglib代理,是aop底層實現(xiàn)過程

          • 織入?就是切面中的方法完成加載執(zhí)行的過程

          這里有8個概念,但真正要完成aop的理解,還不得不再引入兩個概念。

          • 被代理對象?我們可以看到,上面說到目標對象永遠是一個被代理的對象,也是被通知的對象。

          • 代理對象?代理對象呢, 就是最后通知后,生成的對象。

          切入點表達式

          • execution
            用于匹配指定類型內(nèi)的方法執(zhí)行,匹配的是方法,可以確切到方法

          execution(modifiers-pattern??ret-type-pattern?declaring-type-pattern??name-pattern?(param-pattern)
          ??????????throws-pattern?)
          ??????????
          modifiers-pattern?修飾符表達式???:public?protect?private?,可缺省,表示不限制
          ret-type-pattern????返回值表達式???如?String代表返回值為String?,*代表任意返回值都可以???必填字段
          declaring-type-pattern??類型,可以由完整包名加類名組成???可以只寫包名加.*限定包下的所有類???可缺省,表示不限制
          name-pattern????方法名表達式,可以由*統(tǒng)配所有字符???必填字段
          param-pattern???參數(shù)列表,可以用..來表示所有的方法??必填字段
          execution(public?*?*?(..))???//所有public的方法
          execution(*?set*(..))??//所有set開頭的方法
          execution(?*?com.xyz.service.AccountService.*?(..)?)?//AccountService的所有方法,如果AccountService是接口,指實現(xiàn)了這個接口的所有方法
          execution(*?com.xyz.service.*.*(..))?//com.xyz.service包下的所有類的所有方法
          execution(*?com.xyz.service..*.*(..))?//com.xyz.service包及**子包**下的所有類的所有方法
          • within
            用于匹配指定類型內(nèi)的方法執(zhí)行,匹配的是類型內(nèi)的方法,類型下的所有方法

          within?(com.xyz.service.*)?//?com.xyz.service包下面的所有類的所有方法
          within?(com.xyz.service..*)?//?com.xyz.service包及**子包**下面的所有類的所有方法
          within?(com.xyz.service.impl.UserServiceImpl)?//?UserServiceImpl類下面所有方法
          • this
            用于匹配當前AOP代理對象類型的執(zhí)行方法,在前文中
            引入(Introduction)的代理對象使用,可以注入代理對象

          • bean
            指定spring容器中特定名稱的bean的所有方法為連接點

          • target
            用于匹配當前目標對象類型的執(zhí)行方法,可以注入目標對象,被代理的對象

          • args
            用于匹配當前執(zhí)行的方法傳入的參數(shù)為指定類型的執(zhí)行方法,可以注入連接點(方法)的參數(shù)列表

          • @target 暫未解讀

          • @args 暫未解讀

          • @within 暫未解讀

          • @annotation 暫未解讀

          代碼實戰(zhàn)

          5種通知的案例

          • DemoAspect.java
            定義切面,及通知,這里為了測試更多的案例,表達式切到AdviceKindTestController.java

          package?demo.aop.aspect;

          import?lombok.extern.slf4j.Slf4j;
          import?org.aspectj.lang.ProceedingJoinPoint;
          import?org.aspectj.lang.annotation.*;
          import?org.springframework.stereotype.Component;

          @Aspect
          @Slf4j
          @Component?//必須是個bean
          public?class?DemoAspect?{

          ????//前置通知
          ????@Before("execution?(*?demo.aop.controller.AdviceKindTestController.*(..))")
          ????public?void?auth()?{
          ????????log.info("前置通知,假裝校驗了個權(quán)限");
          ????}

          ????//后置通知
          ????@AfterReturning("execution?(*?demo.aop.controller.AdviceKindTestController.*(..))")
          ????public??Object??afterSomething(){
          ????????log.info("后置通知,不太清楚運用場景");
          ????????return?"ok";
          ????}

          ????//環(huán)繞通知
          ????//如果環(huán)繞通知?不返回執(zhí)行結(jié)果??方法不會返回任何結(jié)果,導致接口拿不到任何數(shù)據(jù)
          ????//所以一定把proceed?返回
          ????//ProceedingJoinPoint?是?JoinPoint的子類,僅當環(huán)繞通知的時候,可以注入ProceedingJoinPoint的連接點
          ????@Around("execution?(*?demo.aop.controller.AdviceKindTestController.*(..))")
          ????public??Object??getMethodTime(ProceedingJoinPoint?point)?throws?Throwable?{
          ????????log.info("環(huán)繞通知,統(tǒng)計方法耗時,方法執(zhí)行前");
          ????????Long?beforeMillis?=?System.currentTimeMillis();
          ????????Object?proceed?=?point.proceed();
          ????????Long?taketimes=?System.currentTimeMillis()-beforeMillis;
          ????????log.info(String.format("該方法用時%s毫秒",taketimes));
          ????????return?proceed;
          ????}

          ????//異常通知
          ????@AfterThrowing("execution?(*?demo.aop.controller.AdviceKindTestController.*(..))")
          ????public?void?throwSomething()?{
          ????????log.info("異常通知,只有異常了才會通知。具體場景,不是特別了解");
          ????}

          ????//最終通知
          ????@After("execution?(*?demo.aop.controller.AdviceKindTestController.*(..))")
          ????public?void?closeSomething()?{
          ????????log.info("最終通知,官網(wǎng)說,可以用來回收某些資源。無論發(fā)不發(fā)生異常,都會被執(zhí)行");
          ????}

          }

          • AdviceKindTestController.java

          測試用的接口類

          package?demo.aop.controller;

          import?org.springframework.web.bind.annotation.GetMapping;
          import?org.springframework.web.bind.annotation.RestController;

          @RestController
          public?class?AdviceKindTestController?{

          ????@GetMapping("/advice")?//http://localhost:8080/advice
          ????public?String?test()?throws?InterruptedException?{
          ????????Thread.sleep(4);
          ????????return?"ok";
          ????}


          ????@GetMapping("/advice/throwing")?//http://localhost:8080/advice/throwing
          ????public?String?test2(){
          ????????int?i=1/0;
          ????????return?"ok";
          ????}
          }

          訪問?http://localhost:8080/advice?后臺輸出為

          2020-10-18?11:24:01.201?????????????????:?環(huán)繞通知,統(tǒng)計方法耗時,方法執(zhí)行前
          2020-10-18?11:24:01.201?????????????????:?前置通知,假裝校驗了個權(quán)限
          2020-10-18?11:24:01.201?????????????????:?該方法用時6毫秒
          2020-10-18?11:24:01.202?????????????????:?最終通知,官網(wǎng)說,可以用來回收某些資源。無論發(fā)不發(fā)生異常,都會被執(zhí)行
          2020-10-18?11:24:01.202?????????????????:?后置通知,不太清楚運用場景

          執(zhí)行順序

          -?環(huán)繞通知的前面部分
          -?前置通知
          -?環(huán)繞通知的后面部分
          -?最終通知
          -?后置通知

          訪問?http://localhost:8080/advice/throwing?后臺輸出為

          2020-10-18?11:30:34.935?????????????????:?環(huán)繞通知,統(tǒng)計方法耗時,方法執(zhí)行前
          2020-10-18?11:30:34.935?????????????????:?前置通知,假裝校驗了個權(quán)限
          2020-10-18?11:30:34.936?????????????????:?最終通知,官網(wǎng)說,可以用來回收某些資源。無論發(fā)不發(fā)生異常,都會被執(zhí)行
          2020-10-18?11:30:34.936?????????????????:?異常通知,只有異常了才會通知。具體場景,不是特別了解
          java.lang.ArithmeticException:?/?by?zero????//test2()方法拋出了異常

          執(zhí)行順序如下,我們可以看到因為接口出現(xiàn)了異常,所以后置通知并沒執(zhí)行,環(huán)繞通知的后面部分也沒執(zhí)行,但最終通知異常通知被執(zhí)行

          -?環(huán)繞通知的前面部分
          -?前置通知
          -?最終通知
          -?異常通知







          粉絲福利:108本java從入門到大神精選電子書領(lǐng)取

          ???

          ?長按上方鋒哥微信二維碼?2 秒
          備注「1234」即可獲取資料以及
          可以進入java1234官方微信群



          感謝點贊支持下哈?

          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  A视频免费观看 | 91 丝袜一区二区三区 | 操在线小视频 | 大香蕉亚洲成人 | 天天肏|