<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 Boot 中的切面AOP處理

          共 6500字,需瀏覽 13分鐘

           ·

          2021-02-08 17:11

          本來已收錄到我寫的10萬字Springboot經(jīng)典學習筆記中,筆記在持續(xù)更新……文末有領取方式

          1. 什么是AOP

          AOP:Aspect Oriented Programming 的縮寫,意為:面向切面編程。面向切面編程的目標就是分離關注點。什么是關注點呢?就是關注點,就是你要做的事情。假如你是一位公子哥,沒啥人生目標,每天衣來伸手,飯來張口,整天只知道一件事:玩(這就是你的關注點,你只要做這一件事)!但是有個問題,你在玩之前,你還需要起床、穿衣服、穿鞋子、疊被子、做早飯等等等等,但是這些事情你不想關注,也不用關注,你只想想玩,那么怎么辦呢?

          對!這些事情通通交給下人去干。你有一個專門的仆人 A 幫你穿衣服,仆人 B 幫你穿鞋子,仆人 C 幫你疊好被子,仆人 D 幫你做飯,然后你就開始吃飯、去玩(這就是你一天的正事),你干完你的正事之后,回來,然后一系列仆人又開始幫你干這個干那個,然后一天就結束了!

          這就是 AOP。AOP 的好處就是你只需要干你的正事,其它事情別人幫你干。也許有一天,你想裸奔,不想穿衣服,那么你把仆人 A 解雇就是了!也許有一天,出門之前你還想帶點錢,那么你再雇一個仆人 E 專門幫你干取錢的活!這就是AOP。每個人各司其職,靈活組合,達到一種可配置的、可插拔的程序結構。

          2. Spring Boot 中的 AOP 處理

          2.1 AOP 依賴

          使用AOP,首先需要引入AOP的依賴。

          <dependency>
          ?<groupId>org.springframework.bootgroupId>
          ?<artifactId>spring-boot-starter-aopartifactId>
          dependency>

          2.2 實現(xiàn) AOP 切面

          Spring Boot 中使用 AOP 非常簡單,假如我們要在項目中打印一些 log,在引入了上面的依賴之后,我們新建一個類 LogAspectHandler,用來定義切面和處理方法。只要在類上加個@Aspect注解即可。@Aspect 注解用來描述一個切面類,定義切面類的時候需要打上這個注解。@Component 注解讓該類交給 Spring 來管理。

          @Aspect
          @Component
          public?class?LogAspectHandler?{

          }

          這里主要介紹幾個常用的注解及使用:

          1.@Pointcut:定義一個切面,即上面所描述的關注的某件事入口。
          2.@Before:在做某件事之前做的事。
          3.@After:在做某件事之后做的事。
          4.@AfterReturning:在做某件事之后,對其返回值做增強處理。
          5.@AfterThrowing:在做某件事拋出異常時,處理。

          2.2.1 @Pointcut 注解

          @Pointcut 注解:用來定義一個切面(切入點),即上文中所關注的某件事情的入口。切入點決定了連接點關注的內容,使得我們可以控制通知什么時候執(zhí)行。

          @Aspect
          @Component
          public?class?LogAspectHandler?{

          ????/**
          ?????*?定義一個切面,攔截com.itcodai.course09.controller包和子包下的所有方法
          ?????*/

          ????@Pointcut("execution(*?com.itcodai.course09.controller..*.*(..))")
          ????public?void?pointCut()?{}
          }

          @Pointcut 注解指定一個切面,定義需要攔截的東西,這里介紹兩個常用的表達式:一個是使用 execution(),另一個是使用 annotation()
          execution(* com.itcodai.course09.controller..*.*(..))) 表達式為例,語法如下:

          execution() 為表達式主體
          第一個 * 號的位置:表示返回值類型,* 表示所有類型
          包名:表示需要攔截的包名,后面的兩個句點表示當前包和當前包的所有子包,com.itcodai.course09.controller 包、子包下所有類的方法
          第二個 * 號的位置:表示類名,* 表示所有類
          *(..) :這個星號表示方法名,* 表示所有的方法,后面括弧里面表示方法的參數(shù),兩個句點表示任何參數(shù)

          annotation() 方式是針對某個注解來定義切面,比如我們對具有@GetMapping注解的方法做切面,可以如下定義切面:

          @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
          public?void?annotationCut()?{}

          然后使用該切面的話,就會切入注解是 @GetMapping 的方法。因為在實際項目中,可能對于不同的注解有不同的邏輯處理,比如 @GetMapping@PostMapping@DeleteMapping 等。所以這種按照注解的切入方式在實際項目中也很常用。

          2.2.2 @Before 注解

          @Before 注解指定的方法在切面切入目標方法之前執(zhí)行,可以做一些 log 處理,也可以做一些信息的統(tǒng)計,比如獲取用戶的請求 url 以及用戶的 ip 地址等等,這個在做個人站點的時候都能用得到,都是常用的方法。例如:

          @Aspect
          @Component
          public?class?LogAspectHandler?{

          ????private?final?Logger?logger?=?LoggerFactory.getLogger(this.getClass());

          ????/**
          ?????*?在上面定義的切面方法之前執(zhí)行該方法
          ?????*?@param?joinPoint?jointPoint
          ?????*/

          ????@Before("pointCut()")
          ????public?void?doBefore(JoinPoint?joinPoint)?{
          ????????logger.info("====doBefore方法進入了====");

          ????????//?獲取簽名
          ????????Signature?signature?=?joinPoint.getSignature();
          ????????//?獲取切入的包名
          ????????String?declaringTypeName?=?signature.getDeclaringTypeName();
          ????????//?獲取即將執(zhí)行的方法名
          ????????String?funcName?=?signature.getName();
          ????????logger.info("即將執(zhí)行方法為:?{},屬于{}包",?funcName,?declaringTypeName);
          ????????
          ????????//?也可以用來記錄一些信息,比如獲取請求的url和ip
          ????????ServletRequestAttributes?attributes?=?(ServletRequestAttributes)?RequestContextHolder.getRequestAttributes();
          ????????HttpServletRequest?request?=?attributes.getRequest();
          ????????//?獲取請求url
          ????????String?url?=?request.getRequestURL().toString();
          ????????//?獲取請求ip
          ????????String?ip?=?request.getRemoteAddr();
          ????????logger.info("用戶請求的url為:{},ip地址為:{}",?url,?ip);
          ????}
          }

          JointPoint 對象很有用,可以用它來獲取一個簽名,然后利用簽名可以獲取請求的包名、方法名,包括參數(shù)(通過 joinPoint.getArgs() 獲取)等等。

          2.2.3 @After 注解

          @After 注解和 @Before ?注解相對應,指定的方法在切面切入目標方法之后執(zhí)行,也可以做一些完成某方法之后的 log 處理。

          @Aspect
          @Component
          public?class?LogAspectHandler?{

          ????private?final?Logger?logger?=?LoggerFactory.getLogger(this.getClass());

          ????/**
          ?????*?定義一個切面,攔截com.itcodai.course09.controller包下的所有方法
          ?????*/

          ????@Pointcut("execution(*?com.itcodai.course09.controller..*.*(..))")
          ????public?void?pointCut()?{}

          ????/**
          ?????*?在上面定義的切面方法之后執(zhí)行該方法
          ?????*?@param?joinPoint?jointPoint
          ?????*/

          ????@After("pointCut()")
          ????public?void?doAfter(JoinPoint?joinPoint)?{

          ????????logger.info("====doAfter方法進入了====");
          ????????Signature?signature?=?joinPoint.getSignature();
          ????????String?method?=?signature.getName();
          ????????logger.info("方法{}已經(jīng)執(zhí)行完",?method);
          ????}
          }

          到這里,我們來寫一個 Controller 來測試一下執(zhí)行結果,新建一個 AopController 如下:

          @RestController
          @RequestMapping("/aop")
          public?class?AopController?{

          ????@GetMapping("/{name}")
          ????public?String?testAop(@PathVariable?String?name)?{
          ????????return?"Hello?"?+?name;
          ????}
          }

          啟動項目,在瀏覽器中輸入 localhost:8080/aop/CSDN,觀察一下控制臺的輸出信息:

          ====doBefore方法進入了====??
          即將執(zhí)行方法為:?testAop,屬于com.itcodai.course09.controller.AopController包??
          用戶請求的url為:http://localhost:8080/aop/name,ip地址為:0:0:0:0:0:0:0:1 ?
          ====doAfter方法進入了====??
          方法testAop已經(jīng)執(zhí)行完

          從打印出來的 log 中可以看出程序執(zhí)行的邏輯與順序,可以很直觀的掌握 @Before@After 兩個注解的實際作用。

          2.2.4 @AfterReturning 注解

          @AfterReturning 注解和 @After 有些類似,區(qū)別在于 @AfterReturning 注解可以用來捕獲切入方法執(zhí)行完之后的返回值,對返回值進行業(yè)務邏輯上的增強處理,例如:

          @Aspect
          @Component
          public?class?LogAspectHandler?{

          ????private?final?Logger?logger?=?LoggerFactory.getLogger(this.getClass());

          ????/**
          ?????*?在上面定義的切面方法返回后執(zhí)行該方法,可以捕獲返回對象或者對返回對象進行增強
          ?????*?@param?joinPoint?joinPoint
          ?????*?@param?result?result
          ?????*/

          ????@AfterReturning(pointcut?=?"pointCut()",?returning?=?"result")
          ????public?void?doAfterReturning(JoinPoint?joinPoint,?Object?result)?{

          ????????Signature?signature?=?joinPoint.getSignature();
          ????????String?classMethod?=?signature.getName();
          ????????logger.info("方法{}執(zhí)行完畢,返回參數(shù)為:{}",?classMethod,?result);
          ????????//?實際項目中可以根據(jù)業(yè)務做具體的返回值增強
          ????????logger.info("對返回參數(shù)進行業(yè)務上的增強:{}",?result?+?"增強版");
          ????}
          }

          需要注意的是:在 @AfterReturning注解 中,屬性 returning 的值必須要和參數(shù)保持一致,否則會檢測不到。該方法中的第二個入?yún)⒕褪潜磺蟹椒ǖ姆祷刂担?doAfterReturning 方法中可以對返回值進行增強,可以根據(jù)業(yè)務需要做相應的封裝。我們重啟一下服務,再測試一下(多余的 log 我不貼出來了):

          方法testAop執(zhí)行完畢,返回參數(shù)為:Hello CSDN ?
          對返回參數(shù)進行業(yè)務上的增強:Hello CSDN增強版

          2.2.5 @AfterThrowing 注解

          顧名思義,@AfterThrowing 注解是當被切方法執(zhí)行時拋出異常時,會進入 @AfterThrowing 注解的方法中執(zhí)行,在該方法中可以做一些異常的處理邏輯。要注意的是 throwing 屬性的值必須要和參數(shù)一致,否則會報錯。該方法中的第二個入?yún)⒓礊閽伋龅漠惓!?/p>

          /**
          ?*?使用AOP處理log
          ?*?@author?shengwu?ni
          ?*?@date?2018/05/04?20:24
          ?*/

          @Aspect
          @Component
          public?class?LogAspectHandler?{

          ????private?final?Logger?logger?=?LoggerFactory.getLogger(this.getClass());

          ????/**
          ?????*?在上面定義的切面方法執(zhí)行拋異常時,執(zhí)行該方法
          ?????*?@param?joinPoint?jointPoint
          ?????*?@param?ex?ex
          ?????*/

          ????@AfterThrowing(pointcut?=?"pointCut()",?throwing?=?"ex")
          ????public?void?afterThrowing(JoinPoint?joinPoint,?Throwable?ex)?{
          ????????Signature?signature?=?joinPoint.getSignature();
          ????????String?method?=?signature.getName();
          ????????//?處理異常的邏輯
          ????????logger.info("執(zhí)行方法{}出錯,異常為:{}",?method,?ex);
          ????}
          }

          該方法我就不測試了,大家可以自行測試一下。

          3. 總結

          本節(jié)課針對 Spring Boot 中的切面 AOP 做了詳細的講解,主要介紹了 Spring Boot 中 AOP 的引入,常用注解的使用,參數(shù)的使用,以及常用 api 的介紹。AOP 在實際項目中很有用,對切面方法執(zhí)行前后都可以根據(jù)具體的業(yè)務,做相應的預處理或者增強處理,同時也可以用作異常捕獲處理,可以根據(jù)具體業(yè)務場景,合理去使用 AOP。

          該文已收錄到我寫的《10萬字Springboot經(jīng)典學習筆記》中,點擊下面小卡片,進入【Java開發(fā)寶典】,回復:筆記,即可免費獲取。


          點贊是最大的支持?aeb2bc38f8a9299265a429aa45f6e300.webp

          瀏覽 28
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  亚洲欧美综合 | 国产99久久九九精品无码免费 | 天天看黄片 | 亚洲国产高清无码 | 日本有码中文字幕 |