1. 程式人生 > >Spring Boot中使用AOP統一處理Web請求日誌

Spring Boot中使用AOP統一處理Web請求日誌

在spring boot中,簡單幾步,使用spring AOP實現一個攔截器:

1、引入依賴:
[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-aop</artifactId>
  4. </dependency>
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2、建立攔截器類(在該類中,定義了攔截規則:攔截com.xjj.web.controller包下面的所有類中,有@RequestMapping註解的方法。):
[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. /** 
  2.  * 攔截器:記錄使用者操作日誌,檢查使用者是否登入…… 
  3.  * @author XuJijun 
  4.  */
  5. @Aspect
  6. @Component
  7. publicclass ControllerInterceptor {  
  8.     privatestaticfinal Logger logger = LoggerFactory.getLogger(ControllerInterceptor.
    class);  
  9.     @Value(“${spring.profiles}”)  
  10.     private String env;  
  11.     /** 
  12.      * 定義攔截規則:攔截com.xjj.web.controller包下面的所有類中,有@RequestMapping註解的方法。 
  13.      */
  14.     @Pointcut(“execution(* com.xjj.web.controller..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)”)  
  15.     publicvoid controllerMethodPointcut(){}  
  16.     /** 
  17.      * 攔截器具體實現 
  18.      * @param pjp 
  19.      * @return JsonResult(被攔截方法的執行結果,或需要登入的錯誤提示。) 
  20.      */
  21.     @Around(“controllerMethodPointcut()”//指定攔截器規則;也可以直接把“execution(* com.xjj………)”寫進這裡
  22.     public Object Interceptor(ProceedingJoinPoint pjp){  
  23.         long beginTime = System.currentTimeMillis();  
  24.         MethodSignature signature = (MethodSignature) pjp.getSignature();  
  25.         Method method = signature.getMethod(); //獲取被攔截的方法
  26.         String methodName = method.getName(); //獲取被攔截的方法名
  27.         Set<Object> allParams = new LinkedHashSet<>(); //儲存所有請求引數,用於輸出到日誌中
  28.         logger.info(”請求開始,方法:{}”, methodName);  
  29.         Object result = null;  
  30.         Object[] args = pjp.getArgs();  
  31.         for(Object arg : args){  
  32.             //logger.debug(“arg: {}”, arg);
  33.             if (arg instanceof Map<?, ?>) {  
  34.                 //提取方法中的MAP引數,用於記錄進日誌中
  35.                 @SuppressWarnings(“unchecked”)  
  36.                 Map<String, Object> map = (Map<String, Object>) arg;  
  37.                 allParams.add(map);  
  38.             }elseif(arg instanceof HttpServletRequest){  
  39.                 HttpServletRequest request = (HttpServletRequest) arg;  
  40.                 if(isLoginRequired(method)){  
  41.                     if(!isLogin(request)){  
  42.                         result = new JsonResult(ResultCode.NOT_LOGIN, “該操作需要登入!去登入嗎?\n\n(不知道登入賬號?請聯絡老許。)”null);  
  43.                     }  
  44.                 }  
  45.                 //獲取query string 或 posted form data引數
  46.                 Map<String, String[]> paramMap = request.getParameterMap();  
  47.                 if(paramMap!=null && paramMap.size()>0){  
  48.                     allParams.add(paramMap);  
  49.                 }  
  50.             }elseif(arg instanceof HttpServletResponse){  
  51.                 //do nothing…
  52.             }else{  
  53.                 //allParams.add(arg);
  54.             }  
  55.         }  
  56.         try {  
  57.             if(result == null){  
  58.                 // 一切正常的情況下,繼續執行被攔截的方法
  59.                 result = pjp.proceed();  
  60.             }  
  61.         } catch (Throwable e) {  
  62.             logger.info(”exception: ”, e);  
  63.             result = new JsonResult(ResultCode.EXCEPTION, “發生異常:”+e.getMessage());  
  64.         }  
  65.         if(result instanceof JsonResult){  
  66.             long costMs = System.currentTimeMillis() - beginTime;  
  67.             logger.info(”{}請求結束,耗時:{}ms”, methodName, costMs);  
  68.         }  
  69.         return result;  
  70.     }  
  71.     /** 
  72.      * 判斷一個方法是否需要登入 
  73.      * @param method 
  74.      * @return 
  75.      */
  76.     privateboolean isLoginRequired(Method method){  
  77.         if(!env.equals(“prod”)){ //只有生產環境才需要登入
  78.             returnfalse;  
  79.         }  
  80.         boolean result = true;  
  81.         if(method.isAnnotationPresent(Permission.class)){  
  82.             result = method.getAnnotation(Permission.class).loginReqired();  
  83.         }  
  84.         return result;  
  85.     }  
  86.     //判斷是否已經登入
  87.     privateboolean isLogin(HttpServletRequest request) {  
  88.         returntrue;  
  89.         /*String token = XWebUtils.getCookieByName(request, WebConstants.CookieName.AdminToken); 
  90.         if(“1”.equals(redisOperator.get(RedisConstants.Prefix.ADMIN_TOKEN+token))){ 
  91.             return true; 
  92.         }else { 
  93.             return false; 
  94.         }*/
  95.     }  
  96. }  
/**
 * 攔截器:記錄使用者操作日誌,檢查使用者是否登入……
 * @author XuJijun
 */
@Aspect
@Component
public class ControllerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(ControllerInterceptor.class);

    @Value("${spring.profiles}")
    private String env;

    /**
     * 定義攔截規則:攔截com.xjj.web.controller包下面的所有類中,有@RequestMapping註解的方法。
     */
    @Pointcut("execution(* com.xjj.web.controller..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void controllerMethodPointcut(){}

    /**
     * 攔截器具體實現
     * @param pjp
     * @return JsonResult(被攔截方法的執行結果,或需要登入的錯誤提示。)
     */
    @Around("controllerMethodPointcut()") //指定攔截器規則;也可以直接把“execution(* com.xjj.........)”寫進這裡
    public Object Interceptor(ProceedingJoinPoint pjp){
        long beginTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod(); //獲取被攔截的方法
        String methodName = method.getName(); //獲取被攔截的方法名

        Set<Object> allParams = new LinkedHashSet<>(); //儲存所有請求引數,用於輸出到日誌中

        logger.info("請求開始,方法:{}", methodName);

        Object result = null;

        Object[] args = pjp.getArgs();
        for(Object arg : args){
            //logger.debug("arg: {}", arg);
            if (arg instanceof Map<?, ?>) {
                //提取方法中的MAP引數,用於記錄進日誌中
                @SuppressWarnings("unchecked")
                Map<String, Object> map = (Map<String, Object>) arg;

                allParams.add(map);
            }else if(arg instanceof HttpServletRequest){
                HttpServletRequest request = (HttpServletRequest) arg;
                if(isLoginRequired(method)){
                    if(!isLogin(request)){
                        result = new JsonResult(ResultCode.NOT_LOGIN, "該操作需要登入!去登入嗎?\n\n(不知道登入賬號?請聯絡老許。)", null);
                    }
                }

                //獲取query string 或 posted form data引數
                Map<String, String[]> paramMap = request.getParameterMap();
                if(paramMap!=null && paramMap.size()>0){
                    allParams.add(paramMap);
                }
            }else if(arg instanceof HttpServletResponse){
                //do nothing...
            }else{
                //allParams.add(arg);
            }
        }

        try {
            if(result == null){
                // 一切正常的情況下,繼續執行被攔截的方法
                result = pjp.proceed();
            }
        } catch (Throwable e) {
            logger.info("exception: ", e);
            result = new JsonResult(ResultCode.EXCEPTION, "發生異常:"+e.getMessage());
        }

        if(result instanceof JsonResult){
            long costMs = System.currentTimeMillis() - beginTime;
            logger.info("{}請求結束,耗時:{}ms", methodName, costMs);
        }

        return result;
    }

    /**
     * 判斷一個方法是否需要登入
     * @param method
     * @return
     */
    private boolean isLoginRequired(Method method){
        if(!env.equals("prod")){ //只有生產環境才需要登入
            return false;
        }

        boolean result = true;
        if(method.isAnnotationPresent(Permission.class)){
            result = method.getAnnotation(Permission.class).loginReqired();
        }

        return result;
    }

    //判斷是否已經登入
    private boolean isLogin(HttpServletRequest request) {
        return true;
        /*String token = XWebUtils.getCookieByName(request, WebConstants.CookieName.AdminToken);
        if("1".equals(redisOperator.get(RedisConstants.Prefix.ADMIN_TOKEN+token))){
            return true;
        }else {
            return false;
        }*/
    }
}

3、測試

瀏覽器中輸入:http://localhost:8082/api/admin/login

測試結果:

[plain] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. 2016-07-26 11:58:12,057:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:58) - 請求開始,方法:login  
  2. 2016-07-26 11:58:12,061:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:103) - login請求結束,耗時:8ms  
2016-07-26 11:58:12,057:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:58) - 請求開始,方法:login
2016-07-26 11:58:12,061:INFO http-nio-8082-exec-1 (ControllerInterceptor.java:103) - login請求結束,耗時:8ms

證明攔截器已經生效。