一、AOP是一種編程範式java
二、與語言無關,是一種程序設計思想web
一、面向過程到面向對象spring
二、換個角度看世界,換個姿式處理問題 編程
三、將通用邏輯從業務邏輯中分離出來springboot
我的理解,其實就是日誌體系爲了方便使用,能夠用log4j的思惟去理解下。框架
在pom中添加aop依賴,具體示例以下:spring-boot
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.2.2.RELEASE</version> </dependency>
好比咱們在實際項目中,指望咱們操做的每一步都有日誌輸出,那麼咱們該怎麼作呢,仍是用前面學生信息的源代碼,來進行演示。優化
首先建立一個切面,這裏和spring中的aop其實都同樣的,能夠說是更簡便了,具體示例代碼以下:url
package com.rongrong.springboot.demo.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * @author rongrong * @version 1.0 * @description: * @date 2020/1/6 21:50 */ @Aspect @Component public class HttpAspect { @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void log(){ System.out.println("我執行了!!"); } }
接着啓動項目,調用下查詢接口,控制檯輸出以下圖打印內容,證實成功spa
有before,確定就會有after,即調用時有日誌輸出,調用完也有結果輸出,一來方便本身調試,二來也方便查看報錯,那麼after怎麼寫呢?我猜通常同窗確定都這麼幹。
package com.rongrong.springboot.demo.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * @author rongrong * @version 1.0 * @description: * @date 2020/1/6 21:50 */ @Aspect @Component public class HttpAspect { @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void log(){ System.out.println("我執行了!!"); } @After("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void afterlog(){ System.out.println("我執行了!!"); } }
這樣寫一點毛病也沒有,可是。。。。。。。。。。。。。。。。。。。。哇哈哈哈哈哈哈,你確定會說,我確定不這樣寫,能夠不認可,但有些同窗確定是這樣乾的。
寫代碼的原則,儘可能少寫重複代碼,爲啥呢?維護成本高呀,再就是讓人以爲你的代碼很low逼,看到這你確定不會那麼幹了吧,哈哈哈哈哈。
好了玩笑開完了,咱們能夠這樣,聲明個切點,再切點裏維護想要的切面,具體代碼示例以下:
package com.rongrong.springboot.demo.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * @author rongrong * @version 1.0 * @description: * @date 2020/1/6 21:50 */ @Aspect @Component public class HttpAspect { @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void log(){ } @Before("log()") public void doBefore(){ System.out.println("我執行了!!"); } @After("log()") public void doAfter(){ System.out.println("我執行了!!"); } }
改了下,發現彷佛仍是有點low,都用spring boot框架了,咋還能用System.out.println()輸出呢,那麼怎麼優化呢?
如今僅僅知足了,控制檯輸出內容,可是若是我想要的日誌不是這樣的,最基本的得上面同樣吧,有日期、端口、方法名之類的,即項目啓動時控制檯這樣的日誌纔好看些吧,使用spring boot框架自帶日誌self4j便可解決,具體代碼示例以下:
package com.rongrong.springboot.demo.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * @author rongrong * @version 1.0 * @description: * @date 2020/1/6 21:50 */ @Aspect @Component public class HttpAspect { /** * 使用self4j,此日誌爲spring boot自帶的日誌框架 */ private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class); @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void log(){ } @Before("log()") public void doBefore(){ logger.info("我執行了!!"); } @After("log()") public void doAfter(){ logger.info("我執行了!!"); } }
啓動項目後,以下圖所示,證實咱們成功了
這彷佛看起來好了不少,可是實際工做時候,爲了方便調試須要把咱們請求接口及請求後返回的響應信息,在控制檯輸出,方便咱們調試定位問題,下面咱們來進行演示如何從控制檯輸出這些信息。
使用RequestContextHolder來得到請求參數相關屬性,這裏須要強轉成ServletRequestAttributes對象,Joinpoint這個參數非必須,是在獲取「類方法」、「類名」、「方法參數」的時候會用到,若是用不到的話就不須要了。
具體示例代碼以下所示:
package com.rongrong.springboot.demo.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.jws.Oneway; import javax.servlet.http.HttpServletRequest; /** * @author rongrong * @version 1.0 * @description: * @date 2020/1/6 21:50 */ @Aspect @Component public class HttpAspect { /** * 使用self4j,此日誌爲spring boot自帶的日誌框架 */ private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class); /** *此處爲了簡化代碼,提升維護性,仍是須要提煉下的 @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void log(){ System.out.println("我執行了!!"); } */ @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void log(){ } /** * 在接口執行操做時輸出相關參數 */ @Before("log()") public void doBefore(JoinPoint joinPoint){ //使用RequestContextHolder來得到請求屬性,這裏須要強轉成ServletRequestAttributes對象 ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); //獲取請求url String url = servletRequestAttributes.getRequest().getRequestURI(); //獲取請求IP String addr = servletRequestAttributes.getRequest().getRemoteAddr(); //獲取請求方法 String method = servletRequestAttributes.getRequest().getMethod(); //獲取類名 String pCName = joinPoint.getSignature().getDeclaringTypeName(); //獲取類方法 String cName = joinPoint.getSignature().getName(); //這裏要說明下 logger.info("url={}",url),url爲{}自動填充部分 //url logger.info("url= {}",url); //ip logger.info("ip= {}",addr); //method logger.info("method= {}",method); //args //獲取請求參數 logger.info("args= {}",joinPoint.getArgs()); //類名和類方法 logger.info("類名和類方法= {}",pCName+"."+cName); } @After("log()") public void doAfter(){ logger.info("doAfter :我執行了!!"); } }
從新啓動項目,咱們調用下查詢全部學生接口,控制檯顯示以下信息,證實日誌成功!
接下來咱們再來輸出響應結果信息,使用註解@AfterReturning,獲取返回相應信息,具體示例代碼以下:
package com.rongrong.springboot.demo.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.jws.Oneway; import javax.servlet.http.HttpServletRequest; /** * @author rongrong * @version 1.0 * @description: * @date 2020/1/6 21:50 */ @Aspect @Component public class HttpAspect { /** * 使用self4j,此日誌爲spring boot自帶的日誌框架 */ private final static Logger logger= LoggerFactory.getLogger(HttpAspect.class); /** *此處爲了簡化代碼,提升維護性,仍是須要提煉下的 @Before("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void log(){ System.out.println("我執行了!!"); } */ @Pointcut("execution(public * com.rongrong.springboot.demo.controller.StudentController.*(..))") public void log(){ } /** * 在接口執行操做時輸出相關參數 */ @Before("log()") public void doBefore(JoinPoint joinPoint){ //使用RequestContextHolder來得到請求屬性,這裏須要強轉成ServletRequestAttributes對象 ServletRequestAttributes servletRequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); //獲取請求url String url = servletRequestAttributes.getRequest().getRequestURI(); //獲取請求IP String addr = servletRequestAttributes.getRequest().getRemoteAddr(); //獲取請求方法 String method = servletRequestAttributes.getRequest().getMethod(); //獲取類名 String pCName = joinPoint.getSignature().getDeclaringTypeName(); //獲取類方法 String cName = joinPoint.getSignature().getName(); //這裏要說明下 logger.info("url={}",url),url爲{}自動填充部分 //url logger.info("url= {}",url); //ip logger.info("ip= {}",addr); //method logger.info("method= {}",method); //args //獲取請求參數 logger.info("args= {}",joinPoint.getArgs()); //類名和類方法 logger.info("類名和類方法= {}",pCName+"."+cName); } @After("log()") public void doAfter(){ logger.info("doAfter :我執行了!!"); } /** * 使用@AfterReturning,獲取返回相應信息 */ @AfterReturning(returning = "object",pointcut="log()") public void doAfterReturning(Object object){ logger.info("返回信息 :{}",object.toString()); } }
再次從新啓動項目,咱們調用下查詢全部學生接口,控制檯顯示以下信息,證實日誌成功!
到此,spring boot中Aop的使用分享完畢,有興趣的同窗能夠自行嘗試哦。