1. 程式人生 > >SpringAOP攔截Controller,Service實現日誌管理(自定義註解的方式)(轉載)

SpringAOP攔截Controller,Service實現日誌管理(自定義註解的方式)(轉載)

http://langgufu.iteye.com/blog/2235556

  首先我們為什麼需要做日誌管理,在現實的上線中我們經常會遇到系統出現異常或者問題。這個時候就馬上開啟CRT或者SSH連上伺服器拿日子來分析。受網路的各種限制。於是我們就想為什麼不能直接在管理後臺檢視報錯的資訊呢。於是日誌管理就出現了。

         其次個人覺得做日誌管理最好的是Aop,有的人也喜歡用攔截器。都可以,在此我重點介紹我的實現方式。

         Aop有的人說攔截不到Controller。有的人說想攔AnnotationMethodHandlerAdapter截到Controller必須得攔截org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter。

首先Aop可以攔截到Controller的,這個是毋容置疑的其次須攔截AnnotationMethodHandlerAdapter也不是必須的。最起碼我沒有驗證成功過這個。我的Spring版本是4.0.3。

         Aop之所以有的人說攔截不到Controller是因為Controller被jdk代理了。我們只要把它交給cglib代理就可以了。

第一步定義兩個註解:

Java程式碼複製程式碼
  1. package com.annotation;  
  2. import java.lang.annotation.*;  
  3. /** 
  4.  *自定義註解 攔截Controller 
  5.  */  
  6. @Target({ElementType.PARAMETER, ElementType.METHOD})  
  7. @Retention(RetentionPolicy.RUNTIME)  
  8. @Documented  
  9. public @interface SystemControllerLog {  
  10.     String description() default "";  
  11. }  
  12. package com.annotation;  
  13. import java.lang.annotation.*;  
  14. /** 
  15.  *自定義註解 攔截service 
  16.  */  
  17. @Target({ElementType.PARAMETER, ElementType.METHOD})  
  18. @Retention(RetentionPolicy.RUNTIME)  
  19. @Documented  
  20. public @interface SystemServiceLog {  
  21.     String description() default "";  
  22. }  

第二步建立一個切點類:

Java程式碼複製程式碼
  1. package com.annotation;  
  2. import com.model.Log;  
  3. import com.model.User;  
  4. import com.service.LogService;  
  5. import com.util.DateUtil;  
  6. import com.util.JSONUtil;  
  7. import com.util.SpringContextHolder;  
  8. import com.util.WebConstants;  
  9. import org.aspectj.lang.JoinPoint;  
  10. import org.aspectj.lang.annotation.*;  
  11. import org.slf4j.Logger;  
  12. import org.slf4j.LoggerFactory;  
  13. import org.springframework.stereotype.Component;  
  14. import org.springframework.web.context.request.RequestContextHolder;  
  15. import org.springframework.web.context.request.ServletRequestAttributes;  
  16. import javax.annotation.Resource;  
  17. import javax.servlet.http.HttpServletRequest;  
  18. import javax.servlet.http.HttpSession;  
  19. import java.lang.reflect.Method;  
  20. /** 
  21.  * 切點類 
  22.  * @author tiangai 
  23.  * @since 2014-08-05 Pm 20:35 
  24.  * @version 1.0 
  25.  */  
  26. @Aspect  
  27. @Component  
  28. public class SystemLogAspect {  
  29.     //注入Service用於把日誌儲存資料庫  
  30.     @Resource  
  31.     private LogService logService;  
  32.     //本地異常日誌記錄物件  
  33.     private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);  
  34.     //Service層切點  
  35.     @Pointcut("@annotation(com.annotation.SystemServiceLog)")  
  36.     public void serviceAspect() {  
  37.     }  
  38.     //Controller層切點  
  39.     @Pointcut("@annotation(com.annotation.SystemControllerLog)")  
  40.     public void controllerAspect() {  
  41.     }  
  42.     /** 
  43.      * 前置通知 用於攔截Controller層記錄使用者的操作 
  44.      * 
  45.      * @param joinPoint 切點 
  46.      */  
  47.     @Before("controllerAspect()")  
  48.     public void doBefore(JoinPoint joinPoint) {  
  49.         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
  50.         HttpSession session = request.getSession();  
  51.         //讀取session中的使用者  
  52.         User user = (User) session.getAttribute(WebConstants.CURRENT_USER);  
  53.         //請求的IP  
  54.         String ip = request.getRemoteAddr();  
  55.         try {  
  56.             //*========控制檯輸出=========*//  
  57.             System.out.println("=====前置通知開始=====");  
  58.             System.out.println("請求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
  59.             System.out.println("方法描述:" + getControllerMethodDescription(joinPoint));  
  60.             System.out.println("請求人:" + user.getName());  
  61.             System.out.println("請求IP:" + ip);  
  62.             //*========資料庫日誌=========*//  
  63.             Log log = SpringContextHolder.getBean("logxx");  
  64.             log.setDescription(getControllerMethodDescription(joinPoint));  
  65.             log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
  66.             log.setType("0");  
  67.             log.setRequestIp(ip);  
  68.             log.setExceptionCode(null);  
  69.             log.setExceptionDetail(null);  
  70.             log.setParams(null);  
  71.             log.setCreateBy(user);  
  72.             log.setCreateDate(DateUtil.getCurrentDate());  
  73.             //儲存資料庫  
  74.             logService.add(log);  
  75.             System.out.println("=====前置通知結束=====");  
  76.         } catch (Exception e) {  
  77.             //記錄本地異常日誌  
  78.             logger.error("==前置通知異常==");  
  79.             logger.error("異常資訊:{}", e.getMessage());  
  80.         }  
  81.     }  
  82.     /** 
  83.      * 異常通知 用於攔截service層記錄異常日誌 
  84.      * 
  85.      * @param joinPoint 
  86.      * @param e 
  87.      */  
  88.     @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")  
  89.     public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {  
  90.         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
  91.         HttpSession session = request.getSession();  
  92.         //讀取session中的使用者  
  93.         User user = (User) session.getAttribute(WebConstants.CURRENT_USER);  
  94.         //獲取請求ip  
  95.         String ip = request.getRemoteAddr();  
  96.         //獲取使用者請求方法的引數並序列化為JSON格式字串  
  97.         String params = "";  
  98.         if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {  
  99.             for (int i = 0; i < joinPoint.getArgs().length; i++) {  
  100.                 params += JSONUtil.toJsonString(joinPoint.getArgs()[i]) + ";";  
  101.             }  
  102.         }  
  103.         try {  
  104.               /*========控制檯輸出=========*/  
  105.             System.out.println("=====異常通知開始=====");  
  106.             System.out.println("異常程式碼:" + e.getClass().getName());  
  107.             System.out.println("異常資訊:" + e.getMessage());  
  108.             System.out.println("異常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
  109.             System.out.println("方法描述:" + getServiceMthodDescription(joinPoint));  
  110.             System.out.println("請求人:" + user.getName());  
  111.             System.out.println("請求IP:" + ip);  
  112.             System.out.println("請求引數:" + params);  
  113.                /*==========資料庫日誌=========*/  
  114.             Log log = SpringContextHolder.getBean("logxx");  
  115.             log.setDescription(getServiceMthodDescription(joinPoint));  
  116.             log.setExceptionCode(e.getClass().getName());  
  117.             log.setType("1");  
  118.             log.setExceptionDetail(e.getMessage());  
  119.             log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));  
  120.             log.setParams(params);  
  121.             log.setCreateBy(user);  
  122.             log.setCreateDate(DateUtil.getCurrentDate());  
  123.             log.setRequestIp(ip);  
  124.             //儲存資料庫  
  125.             logService.add(log);  
  126.             System.out.println("=====異常通知結束=====");  
  127.         } catch (Exception ex) {  
  128.             //記錄本地異常日誌  
  129.             logger.error("==異常通知異常==");  
  130.             logger.error("異常資訊:{}", ex.getMessage());  
  131.         }  
  132.          /*==========記錄本地異常日誌==========*/  
  133.         logger.error("異常方法:{}異常程式碼:{}異常資訊:{}引數:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);  
  134.     }  
  135.     /** 
  136.      * 獲取註解中對方法的描述資訊 用於service層註解 
  137.      * 
  138.      * @param joinPoint 切點 
  139.      * @return 方法描述 
  140.      * @throws Exception 
  141.      */  
  142.     public static String getServiceMthodDescription(JoinPoint joinPoint)  
  143.             throws Exception {  
  144.         String targetName = joinPoint.getTarget().getClass().getName();  
  145.         String methodName = joinPoint.getSignature().getName();  
  146.         Object[] arguments = joinPoint.getArgs();  
  147.         Class targetClass = Class.forName(targetName);  
  148.         Method[] methods = targetClass.getMethods();  
  149.         String description = "";  
  150.         for (Method method : methods) {  
  151.             if (method.getName().equals(methodName)) {  
  152.                 Class[] clazzs = method.getParameterTypes();  
  153.                 if (clazzs.length == arguments.length) {  
  154.                     description = method.getAnnotation(SystemServiceLog.class).description();  
  155.                     break;  
  156.                 }  
  157.             }  
  158.         }  
  159.         return description;  
  160.     }  
  161.     /** 
  162.      * 獲取註解中對方法的描述資訊 用於Controller層註解 
  163.      * 
  164.      * @param joinPoint 切點 
  165.      * @return 方法描述 
  166.      * @throws Exception 
  167.      */  
  168.     public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {  
  169.         String targetName = joinPoint.getTarget().getClass().getName();  
  170.         String methodName = joinPoint.getSignature().getName();  
  171.         Object[] arguments = joinPoint.getArgs();  
  172.         Class targetClass = Class.forName(targetName);  
  173.         Method[] methods = targetClass.getMethods();  
  174.         String description = "";  
  175.         for (Method method : methods) {  
  176.             if (method.getName().equals(methodName)) {  
  177.                 Class[] clazzs = method.getParameterTypes();  
  178.                 if (clazzs.length == arguments.length) {  
  179.                     description = method.getAnnotation(SystemControllerLog.class).description();  
  180.                     break;  
  181.                 }  
  182.             }  
  183.         }  
  184.         return description;  
  185.     }  
  186. }  

 第三步把Controller的代理權交給cglib

在例項化ApplicationContext的時候需要加上

Xml程式碼複製程式碼
  1. <!-- 啟動對@AspectJ註解的支援 -->  
  2. <aop:aspectj-autoproxy/>  

 在呼叫Controller的時候AOP發揮作用所以在SpringMVC的配置檔案里加上

Xml程式碼複製程式碼
  1. <!--通知spring使用cglib而不是jdk的來生成代理方法 AOP可以攔截到Controller->  
  2. <aop:aspectj-autoproxy proxy-target-class="true"/>  

 第四步使用

Controller層的使用

Java程式碼複製程式碼
  1. /** 
  2.     * 刪除使用者 
  3.     * 
  4.     * @param criteria 條件 
  5.     * @param id       id 
  6.     * @param model    模型 
  7.     * @return 資料列表 
  8.     */  
  9.    @RequestMapping(value = "/delete")  
  10.    //此處為記錄AOP攔截Controller記錄使用者操作  
  11.    @SystemControllerLog(description = "刪除使用者")  
  12.    public String del(Criteria criteria, String id, Model model, HttpSession session) {  
  13.        try {  
  14.            User user = (User) session.getAttribute(WebConstants.CURRENT_USER);  
  15.            if (null != user) {  
  16.                if (user.getId().equals(id)) {  
  17.                    msg = "您不可以刪除自己!";  
  18.                    criteria = userService.selectByCriteriaPagination(criteria);  
  19.                } else {  
  20.                    //刪除資料並查詢出資料  
  21.                    criteria = userService.delete(id, criteria);  
  22.                    msg = "刪除成功!";  
  23.                }  
  24.            }  
  25.        } catch (Exception e) {  
  26.            msg = "刪除失敗!";  
  27.        } finally {  
  28.            model.addAttribute("msg", msg);  
  29.            model.addAttribute("criteria", criteria);  
  30.        }  
  31.        //跳轉列表頁  
  32.        return "user/list";  
  33.    }  

 Service層的使用

Java程式碼複製程式碼
  1. /** 
  2.     * 按照分頁查詢 
  3.     * @param criteria 
  4.     * @return 
  5.     */  
  6.    //此處為AOP攔截Service記錄異常資訊。方法不需要加try-catch  
  7.    @SystemServiceLog(description = "查詢使用者")  
  8.    public Criteria<User> selectByCriteriaPagination(Criteria<User> criteria)  
  9.    {  
  10.        criteria.getList().get(0).getAccount();  
  11.        //查詢總數  
  12.        long total=userMapper.countByCriteria(criteria);  
  13.        //設定總數  
  14.        criteria.setRowCount(total);  
  15.        criteria.setList(userMapper.selectByCriteriaPagination(criteria));  
  16.        return  criteria;  
  17.    }