经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
Spring AOP实现复杂的日志记录操作(自定义注解)
来源:jb51  时间:2021/9/27 11:05:54  对本文有异议

Spring AOP复杂的日志记录(自定义注解)

做项目中,业务逻辑要求只要对数据库数据进行改动的都需要记录日志(增删改),记录的内容有操作者、操作的表名及表名称、具体的操作,以及操作对应的数据。

首先想到的就是Spring 的AOP功能。可是经过一番了解过后,发现一般的日志记录,只能记录一些简单的操作,例如表名、表名称等记录不到。

于是想到了自定义注解的方法,把想要记录的内容放在注解中,通过切入点来获取注解参数,就能获取自己想要的数据,记录数据库中。顺着这个思路,在网上查找了一些相关资料,最终实现功能。话不多说,以下就是实现的思路及代码:

第一步

在代码中添加自定义注解,并且定义两个属性,一个是日志的描述(description),还有个是操作表类型(tableType),属性参数可按需求改变。代码如下:

  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. /**
  7. * ClassName: SystemServiceLog <br/>
  8. * Function: AOP日志记录,自定义注解 <br/>
  9. * date: 2016年6月7日 上午9:29:01 <br/>
  10. * @author lcma
  11. * @version
  12. * @since JDK 1.7
  13. */
  14. @Target({ElementType.PARAMETER, ElementType.METHOD})
  15. @Retention(RetentionPolicy.RUNTIME)
  16. @Documented
  17. public @interface SystemServiceLog {
  18. /**
  19. * 日志描述
  20. */
  21. String description() default "";
  22. /**
  23. * 操作表类型
  24. */
  25. int tableType() default 0;
  26. }

第二步

定义切面类,获取切面参数,保存数据库具体代码如下:

  1. import java.lang.reflect.Method;
  2. import java.util.Date;
  3. import javax.annotation.Resource;
  4. import javax.servlet.http.HttpServletRequest;
  5. import org.apache.log4j.Logger;
  6. import org.aspectj.lang.JoinPoint;
  7. import org.aspectj.lang.annotation.After;
  8. import org.aspectj.lang.annotation.Aspect;
  9. import org.aspectj.lang.annotation.Pointcut;
  10. import org.springframework.stereotype.Component;
  11. import org.springframework.web.context.request.RequestContextHolder;
  12. import org.springframework.web.context.request.ServletRequestAttributes;
  13. import com.iflytek.zhbs.common.annotation.SystemServiceLog;
  14. import com.iflytek.zhbs.common.util.JacksonUtil;
  15. import com.iflytek.zhbs.common.util.WebUtils;
  16. import com.iflytek.zhbs.dao.BaseDaoI;
  17. import com.iflytek.zhbs.domain.CmsAdmin;
  18. import com.iflytek.zhbs.domain.CmsOperationLog;
  19. @Aspect
  20. @Component
  21. @SuppressWarnings("rawtypes")
  22. public class SystemLogAspect {
  23. @Resource
  24. private BaseDaoI<CmsOperationLog> logDao;
  25. /**
  26. * 日志记录
  27. */
  28. private static final Logger LOGGER = Logger.getLogger(SystemLogAspect.class);
  29. /**
  30. * Service层切点
  31. */
  32. @Pointcut("@annotation(com.iflytek.zhbs.common.annotation.SystemServiceLog)")
  33. public void serviceAspect() {
  34. }
  35. /**
  36. * doServiceLog:获取注解参数,记录日志. <br/>
  37. * @author lcma
  38. * @param joinPoint 切入点参数
  39. * @since JDK 1.7
  40. */
  41. @After("serviceAspect()")
  42. public void doServiceLog(JoinPoint joinPoint) {
  43. LOGGER.info("日志记录");
  44. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  45. //获取管理员用户信息
  46. CmsAdmin admin = WebUtils.getAdminInfo(request);
  47. try {
  48. //数据库日志
  49. CmsOperationLog log = new CmsOperationLog();
  50. log.setOperationType(getServiceMthodTableType(joinPoint));
  51. //获取日志描述信息
  52. String content = getServiceMthodDescription(joinPoint);
  53. log.setContent(admin.getRealName() + content);
  54. log.setRemarks(getServiceMthodParams(joinPoint));
  55. log.setAdmin(admin);
  56. log.setCreateTime(new Date());
  57. logDao.save(log);
  58. } catch (Exception e) {
  59. LOGGER.error("异常信息:{}", e);
  60. }
  61. }
  62. /**
  63. * getServiceMthodDescription:获取注解中对方法的描述信息 用于service层注解 . <br/>
  64. * @author lcma
  65. * @param joinPoint 切点
  66. * @return 方法描述
  67. * @throws Exception
  68. * @since JDK 1.7
  69. */
  70. private String getServiceMthodDescription(JoinPoint joinPoint)
  71. throws Exception {
  72. String targetName = joinPoint.getTarget().getClass().getName();
  73. String methodName = joinPoint.getSignature().getName();
  74. Object[] arguments = joinPoint.getArgs();
  75. Class targetClass = Class.forName(targetName);
  76. Method[] methods = targetClass.getMethods();
  77. String description = "";
  78. for(Method method : methods) {
  79. if(method.getName().equals(methodName)) {
  80. Class[] clazzs = method.getParameterTypes();
  81. if(clazzs.length == arguments.length) {
  82. description = method.getAnnotation(SystemServiceLog.class).description();
  83. break;
  84. }
  85. }
  86. }
  87. return description;
  88. }
  89. /**
  90. * getServiceMthodTableType:获取注解中对方法的数据表类型 用于service层注解 . <br/>
  91. * @author lcma
  92. * @param joinPoint
  93. * @return
  94. * @throws Exception
  95. * @since JDK 1.7
  96. */
  97. private nt getServiceMthodTableType(JoinPoint joinPoint)
  98. throws Exception {
  99. String targetName = joinPoint.getTarget().getClass().getName();
  100. String methodName = joinPoint.getSignature().getName();
  101. Object[] arguments = joinPoint.getArgs();
  102. Class targetClass = Class.forName(targetName);
  103. Method[] methods = targetClass.getMethods();
  104. int tableType = 0;
  105. for (Method method : methods) {
  106. if (method.getName().equals(methodName)) {
  107. Class[] clazzs = method.getParameterTypes();
  108. if (clazzs.length == arguments.length) {
  109. tableType = method.getAnnotation(SystemServiceLog.class).tableType();
  110. break;
  111. }
  112. }
  113. }
  114. return tableType;
  115. }
  116. /**
  117. * getServiceMthodParams:获取json格式的参数. <br/>
  118. * @author lcma
  119. * @param joinPoint
  120. * @return
  121. * @throws Exception
  122. * @since JDK 1.7
  123. */
  124. private String getServiceMthodParams(JoinPoint joinPoint)
  125. throws Exception {
  126. Object[] arguments = joinPoint.getArgs();
  127. String params = JacksonUtil.toJSon(arguments);
  128. return params;
  129. }
  130. }

需要注意的是,定义切点的时候,@Pointcut里面是自定义注解的路径

每个切面传递的数据的都不一样,最终决定,获取切面的所有参数,转成json字符串,保存到数据库中。

第三步

在service需要记录日志的地方进行注解,代码如下:

  1. @SystemServiceLog(description=Constants.ADMIN_SAVE_OPTIONS,tableType=Constants.ADMIM_TABLE_TYPE)

代码图片:

在常量类里面配置自定义注解的参数内容:

第四步

把切面类所在的包路径添加到Spring注解自动扫描路径下,并且启动对@AspectJ注解的支持,代码如下:

  1. <!-- 启动对@AspectJ注解的支持 -->
  2. <aop:aspectj-autoproxy proxy-target-class="true" />
  3. <!-- 自动扫描包路径 -->
  4. <context:component-scan base-package="com.iflytek.zhbs.common.aoplog" />
  5. <context:component-scan base-package="com.iflytek.zhbs.service" />

最后数据库记录数据的效果如图:

OK,功能已经实现,初次写博客,写的不好的地方请谅解。

多个注解可以合并成一个,包括自定义注解

spring中有时候一个类上面标记很多注解。

实际上Java注解可以进行继承(也就是把多个注解合并成1个)

比如说SpringMVC的注解

  1. @RestController
  2. @RequestMapping("/person")

可以合并为一个

  1. @PathRestController("/user")

实现是:

  1. import java.lang.annotation.Documented;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. import org.springframework.core.annotation.AliasFor;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RestController;
  9. @Target(ElementType.TYPE)
  10. @Retention(RetentionPolicy.RUNTIME)
  11. @Documented
  12. @RestController
  13. @RequestMapping
  14. public @interface PathRestController {
  15. @AliasFor("path")
  16. String[] value() default {};
  17. @AliasFor("value")
  18. String[] path() default {};
  19. }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号