在日常开发中经常需要在代码中加入一些记录用户操作日志的log语句,比如谁在什么时间做了什么操作,等等。

把这些对于开发人员开说无关痛痒的代码写死在业务方法中实在不是一件很舒服的事情,于是AOP应运而生。

Spring对AOP的支持有以下4种情况:

1.基于代理的AOP

2.@Aspectj

3.纯POJO

4.注入式Aspectj切面

前三种都是基于方法级的,最后一个可以精确到属性及构造器。

关于Spring对AOP的支持的详细内容,读者可以参考《Spring in Action (第二版)中文版》第四章。

我这里使用的是第三种,纯POJO的方式,这种方式仅能在spring2.0及以后的版本中使用。

ok,言归正传,还是来说一说方法级注解的日志配置方式吧,顾名思义,就是只需要在方法上增加一个注释就可以自动打印日志,所以首先需要创建一个注解,如下:

  1. package com.hqf.common.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. @Target({ElementType.METHOD})
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. public @interface UserOperateLog {
  11. /**
  12. * 用户操作名称
  13. * @return 用户操作名称,默认为空串
  14. */
  15. String value() default "";
  16. /**
  17. * 用户操作类型,默认类型为0<br/>
  18. * 0 - 其他操作 <br/>
  19. * 1 - 查询 <br/>
  20. * 2 - 新增 <br/>
  21. * 3 - 修改 <br/>
  22. * 4 - 删除
  23. * @return 用户操作类型
  24. */
  25. int type() default 0;
  26. /**
  27. * 用户操作名称对应的key,可以通过该key值在属性文件中查找对应的value
  28. * @return key
  29. */
  30. String key() default "";
  31. }

这里只是抛砖引玉,读者可以根据需要建立自己的注解。

有了注解,之后就需要在方法被调用时能解析注解,这就用到了SpringAOP的通知,我这里使用MethodBeforeAdvice,就是在方 法被调用前执行。关于SpringAOP的通知的详细讨论读者可以参考《Spring in Action (第二版)中文版》第四章4.2.1

  1. package com.hqf.common.annotation;
  2. import java.io.FileInputStream;
  3. import java.io.IOException;
  4. import java.lang.reflect.Method;
  5. import java.util.Properties;
  6. import javax.annotation.PostConstruct;
  7. import javax.servlet.http.HttpSession;
  8. import org.apache.log4j.Logger;
  9. import org.springframework.aop.MethodBeforeAdvice;
  10. import org.springframework.core.io.Resource;
  11. import org.springframework.util.Assert;
  12. import org.springframework.util.StringUtils;
  13. public class UserOperateLogAdvisor implements MethodBeforeAdvice {
  14. private Logger logger;//日志句柄
  15. private String loggerName;//日志名称
  16. private Properties properties;//属性文件句柄
  17. /**
  18. * 描述 : <该方法用于初始化属性文件>. <br>
  19. *<p>
  20. 日志内容可以预先配置在配置文件中,在需要打印日志时从配置文件中找到对应的值。
  21. 这 里是做扩展使用,读者可以根据实际情况进行设 计
  22. * @param propertiesFilePath
  23. * @throws IOException
  24. */
  25. public void setPropertiesFilePath(Resource propertiesFilePath)
  26. throws IOException {
  27. if (properties == null)
  28. properties = new Properties();
  29. properties.load(new FileInputStream(propertiesFilePath.getFile()));
  30. }
  31. /*
  32. * (non-Javadoc)
  33. *
  34. * @see
  35. * org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method
  36. * , java.lang.Object[], java.lang.Object)
  37. */
  38. public void before(Method method, Object[] args, Object target)
  39. throws Throwable {
  40. String username = "未知";
  41. for (Object object : args) {
  42. //这里只提供一种获得操作人的方式,既从HttpSession中获取,但这要求方法参数中包含HttpSession
  43. //这里只是抛砖引玉,读者可以根据实际情况进行设计
  44. if (object instanceof HttpSession) {
  45. username = ((HttpSession) object).getAttribute("username") == null ? "未知"
  46. : (String) ((HttpSession) object)
  47. .getAttribute("username");
  48. }
  49. }
  50. //判断方法是否注解了UserOperateLog
  51. UserOperateLog anno = method.getAnnotation(UserOperateLog.class);
  52. if (anno == null)
  53. return;
  54. String defaultMessage = anno.value();
  55. String methodName = target.getClass().getName() + "."
  56. + method.getName();
  57. String desc = this.handleDescription(anno.key(), StringUtils
  58. .hasText(defaultMessage) ? defaultMessage : methodName);
  59. //装配日志信息
  60. String logline = this.buildLogLine(username, anno.type(), desc);
  61. logger.info(logline);
  62. }
  63. /**
  64. * 构建日志行
  65. *
  66. * @param usrname
  67. *            用户名称
  68. * @param operateType
  69. *            操作类型
  70. * @param description
  71. *            操作描述
  72. * @return 日志行: username - operateType - description
  73. */
  74. protected String buildLogLine(String username, int operateType,
  75. String description) {
  76. StringBuilder sb = new StringBuilder();
  77. sb.append(username).append(" - ").append(operateType).append(" - ")
  78. .append(description);
  79. return sb.toString();
  80. }
  81. /**
  82. * 获取日志内容描述,可以从消息配置文件中找到对应的信息
  83. *
  84. * @param key
  85. *            日志内容key
  86. * @param defaultMessage
  87. *            默认的描述信息
  88. * @return 描述信息
  89. */
  90. protected String handleDescription(String key, String defaultMessage) {
  91. if (properties == null)
  92. return defaultMessage;
  93. if (!StringUtils.hasText(key))
  94. return defaultMessage;
  95. String message = properties.getProperty(key);
  96. if (!StringUtils.hasText(message))
  97. return defaultMessage;
  98. else
  99. return message;
  100. }
  101. @PostConstruct
  102. public void init() {
  103. Assert.notNull(loggerName);
  104. logger = Logger.getLogger(loggerName);
  105. }
  106. /**
  107. * @param loggerName
  108. *            the loggerName to set
  109. */
  110. public void setLoggerName(String loggerName) {
  111. this.loggerName = loggerName;
  112. }
  113. }

为了使通知起作用,需要在spring配置文件加入如下内容:

  1. <!-- 定义用户操作日志切入点和通知器 -->
  2. <aop:config proxy-target-class="true">
  3. <aop:pointcut id="operatePoint"
  4. expression="@annotation(com.hqf.common.annotation.UserOperateLog)" />
  5. <aop:advisor pointcut-ref="operatePoint" id="logAdvisor"
  6. advice-ref="userOperateLogAdvisor" />
  7. </aop:config>
  8. <!-- 定义日志文件写入位置,需要在log4j.properties中加入名称为 useroperatorlog的日志配置-->
  9. <bean id="userOperateLogAdvisor" class="com.hqf.common.annotation.UserOperateLogAdvisor"
  10. p:loggerName="useroperatorlog" p:propertiesFilePath="classpath:messages/messages.properties"/>

ok,配置完成,在使用时只需要在方法上加入@UserOperateLog

例如:

  1. @RequestMapping(value = "/demo/index2.do")
  2. @UserOperateLog(value="注解日志",type=1,key="annotation.log")
  3. public String handleIndex2(Model model,HttpSession session){
  4. return "demo/list";
  5. }

日志输出结果如下:

2010-03-04 16:01:45 useroperatorlog:68  INFO - hanqunfeng - 1 - 注解日志

注解里使用了key,这样就会从指定的配置文件中查找,如果查找到就替换掉默认的value值。

详细的代码请参考附件。

Spring2.5那些事之基于AOP的方法级注解式日志配置的更多相关文章

  1. 【Spring开发】—— AOP之方法级拦截

    前言: 前面介绍了Spring的核心模块以及相关的依赖注入等概念.这篇讲解一下spring的另一个重点,AOP面向切面编程. 说道AOP不得不提到几个概念: 切面:也就是我们自己的一些业务方法. 通知 ...

  2. net core天马行空系列:原生DI+AOP实现spring boot注解式编程

    写过spring boot之后,那种无处不在的注解让我非常喜欢,比如属性注入@autowire,配置值注入@value,声明式事物@Transactional等,都非常简洁优雅,那么我就在想,这些在n ...

  3. 基于AOP和ThreadLocal实现日志记录

    基于AOP和ThreadLocal实现的一个日志记录的例子 主要功能实现 : 在API每次被请求时,可以在整个方法调用链路中记录一条唯一的API请求日志,可以记录请求中绝大部分关键内容.并且可以自定义 ...

  4. Spring AOP实现注解式的Mybatis多数据源切换

    一.为什么要使用多数据源切换? 多数据源切换是为了满足什么业务场景?正常情况下,一个微服务或者说一个WEB项目,在使用Mybatis作为数据库链接和操作框架的情况下通常只需要构建一个系统库,在该系统库 ...

  5. 6、架构--Nginx虚拟主机(基于多ip、端口、域名方式)、日志配置、Nginx模块(访问控制模块、状态监控模块、访问链接控制模块)

    笔记 1.晨考 2.昨日问题 3.今日内容 1.Nginx虚拟主机 - 基于多IP的方式 - 基于多端口的方式 - 基于多域名的方式 2.日志配置 Nginx有非常灵活的日志记录模式,每个级别的配置可 ...

  6. Spring基于AOP的事务管理

                                  Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...

  7. 基于AOP的MVC拦截异常让代码更优美

    与asp.net 打交道很多年,如今天微软的优秀框架越来越多,其中微软在基于mvc的思想架构,也推出了自己的一套asp.net mvc 框架,如果你亲身体验过它,会情不自禁的说‘漂亮’.回过头来,‘漂 ...

  8. Java实战之03Spring-05Spring中的事务控制(基于AOP)

    五.Spring中的事务控制(基于AOP) 1.Spring中事务有关的接口 1.1.明确: JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案 1. ...

  9. 从壹开始前后端分离 40 || 完美基于AOP的接口性能分析

    旁白音:本文是不定时更新的.net core,当前主线任务的Nuxt+VueAdmin教程的 nuxt.js 之 tibug项目已上线,大家可以玩一玩:http://123.206.33.109:70 ...

随机推荐

  1. bzoj 4570 妖怪

    bzoj 4570 妖怪 正解应该是 \(O(nlogn)\) 的凸包,但被我的 \(O(100n)\) 的三分水过去了. 记 $x=\frac b a $ ,显然有 \(strength_i=ATK ...

  2. 剑指offer第七章&第八章

    剑指offer第七章&第八章 1.把字符串转换成整数 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数. 数值为0或者字符串不是一个合法的数值则返回0 输入描述: 输入一个字符串 ...

  3. 《DSP using MATLAB》示例Example7.11

    代码: M = 45; As = 60; n = [0:1:M-1]; beta = 0.1102*(As - 8.7) %beta = 0.1102*(As - 8.7) + 0.3 w_kai = ...

  4. 《DSP using MATLAB》示例Example7.2

  5. BZOJ1495 [NOI2006]网络收费

    题意 传送门 MY市NS中学,大概是绵阳市南山中学. 分析 参照Maxwei_wzj的题解. 因为成对的贡献比较难做,我们尝试把贡献算到每一个叶子节点上.我们发现按照题目中的收费方式,它等价于对于每棵 ...

  6. filter添加水印

    1filter写法 先定义自己的responseWrapper chain.doFilter(request,responseWrapper); responseWrapper来输出 package ...

  7. 生产环境LNMP (果图片)

    一. 下载一键安装包 LNMP   官方地址为:http://lnmp.org/ 登陆后运行:screen -S lnmp cd /usr/local/src wget -c http://soft. ...

  8. 函数参数个数不确定时使用va_start

    今天在网上看程序时忽然发现别人的函数参数中有省略号,甚是吃惊,发现其函数中使用了va_start,经过查资料大概明白其用法,个人觉得很好用! #include <stdio.h> #inc ...

  9. linux下踢出已登录用户

    通过xshell登录到linux,看到如下所示,有3个用户,但是前面两个不知在哪登录的了,那就踢出吧. 先确认一下自己是哪个 顺便注意一下“whoami”和“who am i”的不同 然后踢出前面两个 ...

  10. 推动FPGA发展箭在弦上,国内厂商须走差异化之路

    7月25日,由中国电子报与深圳投资推广署共同举办的“第六届(2018)中国FPGA产业发展论坛”在深圳召开. 作为四大通用集成电路芯片之一,FPGA(现场可编程门阵列)的重要性与CPU.存储器.DSP ...