Spring MVC使用AOP實現審計日志


先定一個注解,用於在Controller方法上記錄每個方法的用途。

  1. package com.zjf.spring.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7.  
  8. /**
  9.  * 定義注解
  10.  *
  11. @Target : 用來說明該注解可以被聲明在那些元素之前。
  12.  
  13.    ElementType.TYPE:說明該注解只能被聲明在一個類前。
  14.  
  15.    ElementType.FIELD:說明該注解只能被聲明在一個類的字段前。
  16.  
  17.    ElementType.METHOD:說明該注解只能被聲明在一個類的方法前。
  18.  
  19.    ElementType.PARAMETER:說明該注解只能被聲明在一個方法參數前。
  20.  
  21.    ElementType.CONSTRUCTOR:說明該注解只能聲明在一個類的構造方法前。
  22.  
  23.    ElementType.LOCAL_VARIABLE:說明該注解只能聲明在一個局部變量前。
  24.  
  25.    ElementType.ANNOTATION_TYPE:說明該注解只能聲明在一個注解類型前。
  26.  
  27.    ElementType.PACKAGE:說明該注解只能聲明在一個包名前。
  28.  */
  29. @Target({ElementType.METHOD})
  30.  
  31.  
  32. /**
  33. @Retention :用來說明該注解類的生命周期。它有以下三個參數:
  34.  
  35.    RetentionPolicy.SOURCE : 注解只保留在源文件中
  36.  
  37.    RetentionPolicy.CLASS : 注解保留在class文件中,在加載到JVM虛擬機時丟棄
  38.  
  39.    RetentionPolicy.RUNTIME : 注解保留在程序運行期間,此時可以通過反射獲得定義在某個類上的所有注解。
  40.  */
  41. @Retention(RetentionPolicy.RUNTIME)
  42. /**
  43. Annotation(注解)是JDK5.0及以后版本引入的。它可以用於創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。
  44. 注解是以'@注解名'在代碼中存在的,根據注解參數的個數,我們可以將注解分為:標記注解、單值注解、完整注解三類。
  45. 它們都不會直接影響到程序的語義,只是作為注解(標識)存在,我們可以通過反射機制編程實現對這些元數據(用來描述數據的數據)的訪問。
  46. 另外,你可以在編譯時選擇代碼里的注解是否只存在於源代碼級,或者它也能在class文件中出現。
  47.  */
  48. /*
  49.  * 注解名字 LogAnnotation
  50.  */
  51. public @interface LogAnnotation {
  52.    //為LogAnnotation定義了一個desc屬性 用於描述方法的description 在記錄甚至日志的時候獲取
  53.    String desc() default "無描述信息";
  54. }

 

 

定義切面:

 

  1. package com.zjf.spring.log;
  2.  
  3. import java.lang.reflect.Method;
  4.  
  5. import javax.servlet.http.HttpServletRequest;
  6.  
  7.  
  8.  
  9. import org.aspectj.lang.JoinPoint;
  10. import org.aspectj.lang.Signature;
  11. import org.aspectj.lang.annotation.AfterReturning;
  12. import org.aspectj.lang.annotation.AfterThrowing;
  13. import org.aspectj.lang.annotation.Aspect;
  14. import org.aspectj.lang.annotation.Pointcut;
  15. import org.aspectj.lang.reflect.MethodSignature;
  16. import org.slf4j.Logger;
  17. import org.slf4j.LoggerFactory;
  18. import org.springframework.stereotype.Component;
  19. import org.springframework.web.context.request.RequestContextHolder;
  20. import org.springframework.web.context.request.ServletRequestAttributes;
  21.  
  22. import com.zjf.spring.annotation.LogAnnotation;
  23.  
  24. /**
  25.  *
  26.  * @Aspect生命這個類是一個切面
  27.  *
  28.  */
  29. @Aspect
  30. @Component
  31. public class SystemLogAspect {
  32.    // 注入Service用於把日志保存數據庫
  33.  
  34.    // 本地異常日志記錄對象
  35.    private static final Logger logger = LoggerFactory
  36.          .getLogger(SystemLogAspect.class);
  37.  
  38.    // Controller層切點 通過注解進行切點 凡是生命了LogAnnotation注解的方法都要進入這個切面
  39.    @Pointcut("@annotation(com.zjf.spring.annotation.LogAnnotation)")
  40.    public void controllerAspect() {
  41.  
  42.    }
  43.  
  44.    /**
  45.     *
  46.     * 方法操作成功 會進入@AfterReturning
  47.     * @param joinPoint 代表會記錄切點的信息 就是代碼運行到切點是的變量環境
  48.     * 可以從joinPoint獲取使用的LogAnnotation信息
  49.     * 切點
  50.     */
  51.    @AfterReturning(pointcut = "controllerAspect()")
  52.    public void doBefore(JoinPoint joinPoint) {
  53.       handleLog(joinPoint, null);
  54.    }
  55.  
  56.    /**
  57.     * 方法執行中出現了異常 會出現在@AfterThrowing中
  58.     */
  59.    @AfterThrowing(value = "controllerAspect()", throwing = "e")
  60.    public void doAfter(JoinPoint joinPoint, Exception e) {
  61.       handleLog(joinPoint, e);
  62.    }
  63.  
  64.    private void handleLog(JoinPoint joinPoint,Exception e) {
  65.        try {
  66.            //從joinPoint獲得LogAnnotation注解
  67.           LogAnnotation controllerLog = giveController(joinPoint);
  68.            if(controllerLog == null)
  69.            {
  70.                return;
  71.            }
  72.            //獲取request
  73.            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  74.            //通過request獲取session 然后獲取用戶信息
  75.            //通過request獲取ip
  76.  
  77.            //處理設置注解上的參數 也就是方法的描述
  78.            String desc = controllerLog.desc();
  79.            System.out.println(desc);
  80.            //保存數據庫
  81.  
  82.          } catch (Exception exp) {
  83.           //記錄本地異常日志
  84.           logger.error("==前置通知異常==");
  85.           logger.error("異常信息:{}", exp.getMessage());
  86.           exp.printStackTrace();
  87.          }
  88.    }
  89.  
  90.        /**
  91.         * 是否存在注解,如果存在就記錄日志
  92.         * @param joinPoint
  93.         * @param controllerLog
  94.         * @return
  95.         * @throws Exception
  96.         */
  97.        private static LogAnnotation giveController(JoinPoint joinPoint) throws Exception
  98.        {
  99.           /*
  100.            * JoinPoint可以獲取什么:
  101.             l java.lang.Object[] getArgs():獲取連接點方法運行時的入參列表;
  102.             l Signature getSignature() :獲取連接點的方法簽名對象;
  103.             l java.lang.Object getTarget() :獲取連接點所在的目標對象;
  104.             l java.lang.Object getThis() :獲取代理對象本身;
  105.            */
  106.            Signature signature = joinPoint.getSignature();
  107.            /*
  108.             * MethodSignature可以獲取什么:
  109.                Class getReturnType(); 獲取方法的返回類型
  110.               Method getMethod(); 獲取方法本身
  111.             */
  112.            MethodSignature methodSignature = (MethodSignature) signature;
  113.           /**
  114.            * Method可以獲取方法上的各種信息 比如方法名稱 方法參數 注解 參數上的注解等。
  115.            */
  116.            Method method = methodSignature.getMethod();
  117.  
  118.            if(method != null)
  119.            {
  120.                return method.getAnnotation(LogAnnotation.class);
  121.            }
  122.            return null;
  123.        }
  124. }

聲明:aop:aspectj-autoproxy 

關於aop:aspectj-autoproxy:

通過aop命名空間的<aop:aspectj-autoproxy />聲明自動為spring容器中那些配置@aspectJ切面的bean創建代理,織入切面。當然,spring 在內部依舊采用AnnotationAwareAspectJAutoProxyCreator進行自動代理的創建工作,但具體實現的細節已經被<aop:aspectj-autoproxy />隱藏起來了 

<aop:aspectj-autoproxy />有一個proxy-target-class屬性,默認為false,表示使用jdk動態代理織入增強,當配為<aop:aspectj-autoproxy  poxy-target-class="true"/>時,表示使用CGLib動態代理技術織入增強。不過即使proxy-target-class設置為false,如果目標類沒有聲明接口,則spring將自動使用CGLib動態代理。 

  1. <aop:aspectj-autoproxy />

在Controller方法中加入注解:

  1. @RequestMapping(value="/test1")
  2.    @LogAnnotation(desc="test1方法")
  3.    public String test1(HttpServletRequest request)
  4.    {
  5.       request.setAttribute("att1", "val1");
  6.       return "test1";
  7.    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM