SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)

标签: springaop controller service | 发表时间:2014-08-13 22:17 | 作者:tiangai
出处:http://www.iteye.com

         从业近二,三年了,第一次写博客,平时做做脚手架或者架构一些基础框架然后给大家使用或者自己总结翻译一些文档。虽然是第一次但是我还是要拿Spring开刀。希望张开涛,涛兄看到的时候不要喷我,给我一点指导。

         首先我们为什么需要做日志管理,在现实的上线中我们经常会遇到系统出现异常或者问题。这个时候就马上打开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.   
  3. import java.lang.annotation.*;  
  4.   
  5. /** 
  6.  *自定义注解 拦截Controller 
  7.  */  
  8.   
  9. @Target({ElementType.PARAMETER, ElementType.METHOD})  
  10. @Retention(RetentionPolicy.RUNTIME)  
  11. @Documented  
  12. public  @interface SystemControllerLog {  
  13.   
  14.     String description()  default "";  
  15.   
  16.   
  17. }  
  18.   
  19. package com.annotation;  
  20.   
  21. import java.lang.annotation.*;  
  22.   
  23. /** 
  24.  *自定义注解 拦截service 
  25.  */  
  26.   
  27. @Target({ElementType.PARAMETER, ElementType.METHOD})  
  28. @Retention(RetentionPolicy.RUNTIME)  
  29. @Documented  
  30. public  @interface SystemServiceLog {  
  31.   
  32.     String description()  default "";  
  33.   
  34.   
  35. }  

第二步创建一个切点类:

 

 

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

 第三步把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.    }  

效果图

 

用户操作:



 异常



 初次写博客,写的不好敬请见谅。

下一篇将讲解微信6大消息接口的封装及开源我的封装敬请关注



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [springaop controller service] 推荐:

SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)

- - 企业架构 - ITeye博客
         从业近二,三年了,第一次写博客,平时做做脚手架或者架构一些基础框架然后给大家使用或者自己总结翻译一些文档. 虽然是第一次但是我还是要拿Spring开刀. 希望张开涛,涛兄看到的时候不要喷我,给我一点指导.          首先我们为什么需要做日志管理,在现实的上线中我们经常会遇到系统出现异常或者问题.

Spring3 Mybatis3 freemarker 自动生成对应表的model、mapper、service、controller并自动修改mybatis配

- - 互联网 - ITeye博客
自己利用spring3、mybatis3进行开发时,前期花费了大量的时间去写对应的model、mapper、service文件,并想到用freemarker来动态生成对应的JAVA文件..   其实就是将数据库中的表取出来,表名作为类名,并把对应的列名取出来,作为字段名称,然后通过 freemarker定制的模版去生成相关的文件即可.

Spring MVC Controller单例陷阱

- - 企业架构 - ITeye博客
Spring MVC Controller单例陷阱. 标签:Spring mvc. 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明. Spring MVC Controller默认是单例的:. 1、这个不用废话了,单例不用每次都new,当然快了. 2、不需要实例会让很多人迷惑,因为spring mvc官方也没明确说不可以多例.

SPRING BOOT OAUTH2 + KEYCLOAK - service to service call

- - BlogJava-首页技术区
employee-service调用department-service,如果要按OAUTH2.0流程,只需要提供client-id和client-secrect即可. 在KEYCLOAK中引入service-account,即配置该employee-service时,取消standard-flow,同时激活service-account.

Web Service入门

- - 博客 - 伯乐在线
本文来自文章作者 @Jeremy黄国华 的投稿. 伯乐在线也欢迎其他朋友投稿,投稿时记得留下您的新浪微博账号哦~. 目前对Web Service没有统一的定义,定义一:Web Service是自包含的、模块化的应用程序,它可以在Web中被描述、发布、查找以及调用. 定义二:Web Service是基于网络的、分布式的模块化组件,它执行特定的任务,遵守具体的技术规范,这些规范使得Web Service能与其他兼任的组件进行操作.

Android Service 详解

- - CSDN博客移动开发推荐文章
一个Service也是一种应用程序组件,它运行在后台以提供某种服务,通常不具有可见的用户界面. 其它的应用程序组件可以启动一个Service,即使在用户切换到另外一个应用程序后,这个Service还是一直会在后台运行. 此外,一个应用程序也可以绑定到一个Service然后使用进程间通信(IPC)方式与Service之间发生交互.

【转】 Service Manager

- - 移动开发 - ITeye博客
android2.3 ---  Service Manager分析. Android系统Binder机制的总管是Service Manager,所有的Server(System Server)都需要向他注册,应用程序需要向其查询相应的服务. 可见其作用是多么的重要,那么我们这里就要重点介绍一下这个.

浅谈 k8s ingress controller 选型 - 知乎

- -
大家好,先简单自我介绍下,我叫厉辉,来自腾讯云. 业余时间比较喜欢开源,现在是Apache APISIX PPMC. 今天我来简单给大家介绍下 K8S Ingress 控制器的选型经验,今天我讲的这些内容需要大家对 K8S 有一定的了解,下面是我的分享. 阅读本文需要熟悉以下基本概念:. 集群:是指容器运行所需云资源的集合,包含了若干台云服务器、负载均衡器等云资源.

SpringAOP实现自动生成日志 【基于java.lang.annotation约定】

- - ITeye博客
           项目中总要发布一下服务供系统内外调用,为了方便排查错误,入参出参都要以日志的形式记录下来. 传统做法:缺点一目了然,参数少的时候只能说还好,参数一多,烦不胜烦,浪费时间.            于是借鉴了AOP的思想,将日志功能做成一个切面,横向切入到业务逻辑前后(方法开始前和结束后).

Web Service的研究

- - CSDN博客系统运维推荐文章
SOA和Web Service. 首先明白SOA和Web Service的关系:. * SOA面向服务架构,用于大型分布式系统的一个概念;. * Web Service是实现SOA的方式之一,不是所有的SOA都是基于Web service的;. * 但Webservice确实为最主流的SOA实现方式,有的人甚至把SOA等同于Webservice.