從零開始SpringCloud Alibaba電商系統(十二)——spring aop記錄用戶操作日誌

零、系列

歡迎來嫖從零開始SpringCloud Alibaba電商系列:

  1. 從零開始SpringCloud Alibaba電商系統(一)——Alibaba與Nacos服務註冊與發現
  2. 從零開始SpringCloud Alibaba電商系統(二)——Nacos配置中心
  3. 從零開始SpringCloud Alibaba電商系統(三)——Sentinel流量防衛兵介紹、流量控制demo
  4. 從零開始SpringCloud Alibaba電商系統(四)——Sentinel的fallback和blockHandler
  5. 從零開始SpringCloud Alibaba電商系統(五)——Feign Demo,Sentinel+Feign實現多節點間熔斷/服務降級
  6. 從零開始SpringCloud Alibaba電商系統(六)——Sentinel規則持久化到Nacos配置中心
  7. 從零開始SpringCloud Alibaba電商系統(七)——Spring Security實現登錄認證、權限控制
  8. 從零開始SpringCloud Alibaba電商系統(八)——用一個好看的Swagger接口文檔
  9. 從零開始SpringCloud Alibaba電商系統(九)——基於Spring Security OAuth2實現SSO-認證服務器(非JWT)
  10. 從零開始SpringCloud Alibaba電商系統(十)——基於Redis Session的認證鑑權
  11. 從零開始SpringCloud Alibaba電商系統(十一)——spring security完善之動態url控制

一、需求簡述

日誌在任何一個系統中都是必不可少的,用戶訪問日誌/操作日誌無論在前臺還是後臺管理都很重要,比如前臺的用戶點擊行爲作爲特徵去做推薦系統,後臺管理的找兇手
一般來說,用戶訪問日誌可以在網關(nginx或其他)這一層就可以記錄下,但是如果想更多的記錄一些業務相關的,還是需要放到我們的邏輯層來。
今天我們就將這些Controller的訪問日誌借用AOP統一記錄起來

二、Spring AOP

AOP相信大家多多少少都瞭解,俗稱面向切面編程的化身、OOP的好伴侶、動態代理的踐行者、Spring的大褲衩子等等。 但是這個東西究竟有多少用用過,這又是一個神奇的話題。

閒話少說,Spring AOP是對AOP概念的一個實現,並非一模一樣的實現,所以我們今天只關注Spring AOP怎麼實現業務。

  1. @Aspect
    aspectj組織提供的註解,用於標識這是一個切面。Spring中將其實現爲一個切面類,在這個類裏面可以去定義PointCut、Advice等。
  2. @PointCut
    切點的規則,即哪些方法是我們要切入的。
    PointCut原意是可以切入方法、字段等任何東西的前後,但是在Spring中,它只能切入一個方法的執行前、後。
    我們在這裏,也只需要它來切入Controller所有的方法。
  3. Advice、@After、@Before
    通知是對PointCut的具體處理邏輯,即我們用PointCut規定了一些要被攔截處理的方法,Advice是對他具體的處理邏輯。
    @Befor、@After、@Around等就是Advice,分別代表在PointCut前處理的邏輯、在PointCut之後處理的邏輯、前後都處理的邏輯。
  4. JointPoint
    JointPoint爲不以註解的方式出現,而是在Advice中作爲參數,即當前被Advice攔截處理的是哪一個具體的方法。

三、實現

  1. 上一章節demo(或一個完善的springboot/cloud項目)的基礎上,增加一個aspect包,增加一個SystemLogAspect類。
    在這裏插入圖片描述
    這個類需要加@Aspect註解。

  2. 聲明一個pointcut。
    我在這裏直接攔截了所有的controller下面的方法,將它們作爲攔截點。
    @pointCut的攔截規則建議直接到spring官網去查:
    https://docs.spring.io/spring/docs/5.3.0-SNAPSHOT/spring-framework-reference/core.html#spring-core

	@Pointcut("within(org.lele.*.controller..*)")
    private void logPointCut(){}
  1. 根據pointcut定義Advice,我這裏分別定義了@Befer、@AfterReturn(帶返回值的after)、@AfterThrowing(拋異常會調用到的after)。
    注意,jointPoint在這裏就是這些Advice的參數,它實質上代表的就是被訪問的那個方法。
    @Before("logPointCut()")
    public void before(JoinPoint jp) {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        UserDTO userDTO = sessionUtils.getCurrentUser();

        System.out.println( (jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + "()——請求信息:") );
        System.out.println("請求地址:" + request.getRemoteAddr());
        System.out.println("請求人:" + userDTO.toString() );
        System.out.println("請求方法:" + (jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + "()"));
        System.out.println("請求參數:" + (jp.getArgs().toString()));
    }

    @AfterReturning(value = "logPointCut()",returning = "result")
    public void afterReturning(JoinPoint jp,Object result) {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        UserDTO userDTO = sessionUtils.getCurrentUser();

        System.out.println( (jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + "()——返回結果:") );
        System.out.println("請求地址:" + request.getRemoteAddr());
        System.out.println("請求人:" + userDTO.toString() );
        System.out.println("返回數據:" + JSONObject.toJSONString(result) );
    }

    @AfterThrowing(value = "logPointCut()",throwing = "e")
    public void afterThrowing(JoinPoint jp,Throwable e) {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        UserDTO userDTO = sessionUtils.getCurrentUser();

        System.out.println( (jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + "()——異常結果:") );
        System.out.println("請求地址:" + request.getRemoteAddr());
        System.out.println("請求人:" + userDTO.toString() );
        System.out.println("異常信息:" + e.getMessage() );
    }

sessionUtils是通過spring security容器獲取當前session的工具類,需要的話可以到下面demo中拿。

  1. 啓動項目,隨意訪問controller下面的一個方法,可以看到控制檯打印日誌。
    在這裏插入圖片描述

四、demo地址

這一次並沒有將日誌保存起來,是因爲筆者想要將這些信息寫入到es中,同時其他日誌也寫入es中,配合kibana,做一個方便的可視化,這個下期在寫。
完整代碼:
https://github.com/flyChineseBoy/lel-mall/tree/master/mall12

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章