spring boot 中AOP的使用

1、AOP統一處理請求日誌

也談AOP

一、AOP是一種編程範式java

二、與語言無關,是一種程序設計思想web

  • 面向切面(AOP)Aspect Oriented Programming
  • 面向對象(OOP)Object Oriented Programming
  • 面向過程(POP) Procedure Oriented Programming

 

再談AOP

一、面向過程到面向對象spring

二、換個角度看世界,換個姿式處理問題 編程

三、將通用邏輯從業務邏輯中分離出來springboot

 

2、處理過程

我的理解,其實就是日誌體系爲了方便使用,能夠用log4j的思惟去理解下。框架

 

3、Aop的實際應用

一、準備工做

在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("我執行了!!");
    }
}

四、Self4j的使用

改了下,發現彷佛仍是有點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("我執行了!!");
    }
}

啓動項目後,以下圖所示,證實咱們成功了

五、請求參數及響應信息控制檯輸出

這彷佛看起來好了不少,可是實際工做時候,爲了方便調試須要把咱們請求接口及請求後返回的響應信息,在控制檯輸出,方便咱們調試定位問題,下面咱們來進行演示如何從控制檯輸出這些信息。

5.一、輸出請求參數信息

使用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 :我執行了!!");
    }

}

從新啓動項目,咱們調用下查詢全部學生接口,控制檯顯示以下信息,證實日誌成功!

5.二、輸出響應信息

接下來咱們再來輸出響應結果信息,使用註解@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的使用分享完畢,有興趣的同窗能夠自行嘗試哦。

相關文章
相關標籤/搜索