在日常开发中经常需要在代码中加入一些记录用户操作日志的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. 【angularJS】简介

    简介 AngularJS 是一个 JavaScript 框架.它可通过 <script> 标签添加到 HTML 页面. AngularJS 通过 指令 扩展了 HTML,且通过 表达式 绑 ...

  2. 【oracle】Oracle安装概述

    一.各种分布式数据库 MS SQL Server是中大型分布式关系型数据库.  Oracle ,DB2(IBM)是超大型分布式关系数据库. MySql是中小型分布式关系型数据库. 二.Oracle O ...

  3. Python中super()和__init__()方法

    采用新式类,要求最顶层的父类一定要继承于object,这样就可以利用super()函数来调用父类的init()等函数, 每个父类都执行且执行一次,并不会出现重复调用的情况.而且在子类的实现中,不用到处 ...

  4. gradle asciidoc 使用

    备注:    次文档参考github 例子   1.环境准备 node npm (yarn) java KindleGen 备注: 具体的安装可以参考网上相关文档,KindleGen 下载地址:htt ...

  5. openresty luarocks 安装以及openssl 问题处理

      1. 安装方式 wget https://luarocks.github.io/luarocks/releases/luarocks-2.4.3.tar.gz tar -xzvf luarocks ...

  6. 使用PHP类库PHPqrCode生成二维码

    PHPqrCode是一个PHP二维码生成类库,利用它可以轻松生成二维码,官网提供了下载和多个演示demo, 查看地址:http://phpqrcode.sourceforge.net/.    下载官 ...

  7. 【备忘】mysql主从设置

    主(master)192.168.1.10机器设置: [root@vm-vagrant mysql]# vi my.cnf [mysqld]节点下添加以下配置server-id=1log-bin=my ...

  8. 框架(yii和thinkphp)中实例化php内置或者扩展中的对象问题

    将php原生语句实例化SphinxClient对象移植到yii2框架中报错 原生语句中这样写: $s = new SphinxClient(); 框架中应该加入反斜杠,这样写: $s = new \S ...

  9. vc中edit控件使用总结

    通过类向导可以生成两种类成员变量,一种是cstring类型,一种是cedit类型.在程序中使用时如果只是简单的获取 edit控件内容,或设置简单的内容建议使用cstring类型成员变量.示例:CStr ...

  10. .NET的URL重写

    [概述] URL重写就是首先获得一个进入的URL请求然后把它重新写成网站可以处理的另一个URL的过程.重写URL是非常有用的一个功能,因为它可以让你提高搜索引擎阅读和索引你的网站的能力:而且在你改变了 ...