Spring2.5那些事之基于AOP的方法级注解式日志配置
在日常开发中经常需要在代码中加入一些记录用户操作日志的log语句,比如谁在什么时间做了什么操作,等等。
把这些对于开发人员开说无关痛痒的代码写死在业务方法中实在不是一件很舒服的事情,于是AOP应运而生。
Spring对AOP的支持有以下4种情况:
1.基于代理的AOP
2.@Aspectj
3.纯POJO
4.注入式Aspectj切面
前三种都是基于方法级的,最后一个可以精确到属性及构造器。
关于Spring对AOP的支持的详细内容,读者可以参考《Spring in Action (第二版)中文版》第四章。
我这里使用的是第三种,纯POJO的方式,这种方式仅能在spring2.0及以后的版本中使用。
ok,言归正传,还是来说一说方法级注解的日志配置方式吧,顾名思义,就是只需要在方法上增加一个注释就可以自动打印日志,所以首先需要创建一个注解,如下:
- package com.hqf.common.annotation;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- @Target({ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface UserOperateLog {
- /**
- * 用户操作名称
- * @return 用户操作名称,默认为空串
- */
- String value() default "";
- /**
- * 用户操作类型,默认类型为0<br/>
- * 0 - 其他操作 <br/>
- * 1 - 查询 <br/>
- * 2 - 新增 <br/>
- * 3 - 修改 <br/>
- * 4 - 删除
- * @return 用户操作类型
- */
- int type() default 0;
- /**
- * 用户操作名称对应的key,可以通过该key值在属性文件中查找对应的value
- * @return key
- */
- String key() default "";
- }
这里只是抛砖引玉,读者可以根据需要建立自己的注解。
有了注解,之后就需要在方法被调用时能解析注解,这就用到了SpringAOP的通知,我这里使用MethodBeforeAdvice,就是在方 法被调用前执行。关于SpringAOP的通知的详细讨论读者可以参考《Spring in Action (第二版)中文版》第四章4.2.1
- package com.hqf.common.annotation;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.lang.reflect.Method;
- import java.util.Properties;
- import javax.annotation.PostConstruct;
- import javax.servlet.http.HttpSession;
- import org.apache.log4j.Logger;
- import org.springframework.aop.MethodBeforeAdvice;
- import org.springframework.core.io.Resource;
- import org.springframework.util.Assert;
- import org.springframework.util.StringUtils;
- public class UserOperateLogAdvisor implements MethodBeforeAdvice {
- private Logger logger;//日志句柄
- private String loggerName;//日志名称
- private Properties properties;//属性文件句柄
- /**
- * 描述 : <该方法用于初始化属性文件>. <br>
- *<p>
- 日志内容可以预先配置在配置文件中,在需要打印日志时从配置文件中找到对应的值。
- 这 里是做扩展使用,读者可以根据实际情况进行设 计
- * @param propertiesFilePath
- * @throws IOException
- */
- public void setPropertiesFilePath(Resource propertiesFilePath)
- throws IOException {
- if (properties == null)
- properties = new Properties();
- properties.load(new FileInputStream(propertiesFilePath.getFile()));
- }
- /*
- * (non-Javadoc)
- *
- * @see
- * org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method
- * , java.lang.Object[], java.lang.Object)
- */
- public void before(Method method, Object[] args, Object target)
- throws Throwable {
- String username = "未知";
- for (Object object : args) {
- //这里只提供一种获得操作人的方式,既从HttpSession中获取,但这要求方法参数中包含HttpSession
- //这里只是抛砖引玉,读者可以根据实际情况进行设计
- if (object instanceof HttpSession) {
- username = ((HttpSession) object).getAttribute("username") == null ? "未知"
- : (String) ((HttpSession) object)
- .getAttribute("username");
- }
- }
- //判断方法是否注解了UserOperateLog
- UserOperateLog anno = method.getAnnotation(UserOperateLog.class);
- if (anno == null)
- return;
- String defaultMessage = anno.value();
- String methodName = target.getClass().getName() + "."
- + method.getName();
- String desc = this.handleDescription(anno.key(), StringUtils
- .hasText(defaultMessage) ? defaultMessage : methodName);
- //装配日志信息
- String logline = this.buildLogLine(username, anno.type(), desc);
- logger.info(logline);
- }
- /**
- * 构建日志行
- *
- * @param usrname
- * 用户名称
- * @param operateType
- * 操作类型
- * @param description
- * 操作描述
- * @return 日志行: username - operateType - description
- */
- protected String buildLogLine(String username, int operateType,
- String description) {
- StringBuilder sb = new StringBuilder();
- sb.append(username).append(" - ").append(operateType).append(" - ")
- .append(description);
- return sb.toString();
- }
- /**
- * 获取日志内容描述,可以从消息配置文件中找到对应的信息
- *
- * @param key
- * 日志内容key
- * @param defaultMessage
- * 默认的描述信息
- * @return 描述信息
- */
- protected String handleDescription(String key, String defaultMessage) {
- if (properties == null)
- return defaultMessage;
- if (!StringUtils.hasText(key))
- return defaultMessage;
- String message = properties.getProperty(key);
- if (!StringUtils.hasText(message))
- return defaultMessage;
- else
- return message;
- }
- @PostConstruct
- public void init() {
- Assert.notNull(loggerName);
- logger = Logger.getLogger(loggerName);
- }
- /**
- * @param loggerName
- * the loggerName to set
- */
- public void setLoggerName(String loggerName) {
- this.loggerName = loggerName;
- }
- }
为了使通知起作用,需要在spring配置文件加入如下内容:
- <!-- 定义用户操作日志切入点和通知器 -->
- <aop:config proxy-target-class="true">
- <aop:pointcut id="operatePoint"
- expression="@annotation(com.hqf.common.annotation.UserOperateLog)" />
- <aop:advisor pointcut-ref="operatePoint" id="logAdvisor"
- advice-ref="userOperateLogAdvisor" />
- </aop:config>
- <!-- 定义日志文件写入位置,需要在log4j.properties中加入名称为 useroperatorlog的日志配置-->
- <bean id="userOperateLogAdvisor" class="com.hqf.common.annotation.UserOperateLogAdvisor"
- p:loggerName="useroperatorlog" p:propertiesFilePath="classpath:messages/messages.properties"/>
ok,配置完成,在使用时只需要在方法上加入@UserOperateLog
例如:
- @RequestMapping(value = "/demo/index2.do")
- @UserOperateLog(value="注解日志",type=1,key="annotation.log")
- public String handleIndex2(Model model,HttpSession session){
- return "demo/list";
- }
日志输出结果如下:
2010-03-04 16:01:45 useroperatorlog:68 INFO - hanqunfeng - 1 - 注解日志
注解里使用了key,这样就会从指定的配置文件中查找,如果查找到就替换掉默认的value值。
详细的代码请参考附件。
- AnnotationLog.rar (5.4 MB)
- 下载次数: 385
Spring2.5那些事之基于AOP的方法级注解式日志配置的更多相关文章
- 【Spring开发】—— AOP之方法级拦截
前言: 前面介绍了Spring的核心模块以及相关的依赖注入等概念.这篇讲解一下spring的另一个重点,AOP面向切面编程. 说道AOP不得不提到几个概念: 切面:也就是我们自己的一些业务方法. 通知 ...
- net core天马行空系列:原生DI+AOP实现spring boot注解式编程
写过spring boot之后,那种无处不在的注解让我非常喜欢,比如属性注入@autowire,配置值注入@value,声明式事物@Transactional等,都非常简洁优雅,那么我就在想,这些在n ...
- 基于AOP和ThreadLocal实现日志记录
基于AOP和ThreadLocal实现的一个日志记录的例子 主要功能实现 : 在API每次被请求时,可以在整个方法调用链路中记录一条唯一的API请求日志,可以记录请求中绝大部分关键内容.并且可以自定义 ...
- Spring AOP实现注解式的Mybatis多数据源切换
一.为什么要使用多数据源切换? 多数据源切换是为了满足什么业务场景?正常情况下,一个微服务或者说一个WEB项目,在使用Mybatis作为数据库链接和操作框架的情况下通常只需要构建一个系统库,在该系统库 ...
- 6、架构--Nginx虚拟主机(基于多ip、端口、域名方式)、日志配置、Nginx模块(访问控制模块、状态监控模块、访问链接控制模块)
笔记 1.晨考 2.昨日问题 3.今日内容 1.Nginx虚拟主机 - 基于多IP的方式 - 基于多端口的方式 - 基于多域名的方式 2.日志配置 Nginx有非常灵活的日志记录模式,每个级别的配置可 ...
- Spring基于AOP的事务管理
Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...
- 基于AOP的MVC拦截异常让代码更优美
与asp.net 打交道很多年,如今天微软的优秀框架越来越多,其中微软在基于mvc的思想架构,也推出了自己的一套asp.net mvc 框架,如果你亲身体验过它,会情不自禁的说‘漂亮’.回过头来,‘漂 ...
- Java实战之03Spring-05Spring中的事务控制(基于AOP)
五.Spring中的事务控制(基于AOP) 1.Spring中事务有关的接口 1.1.明确: JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案 1. ...
- 从壹开始前后端分离 40 || 完美基于AOP的接口性能分析
旁白音:本文是不定时更新的.net core,当前主线任务的Nuxt+VueAdmin教程的 nuxt.js 之 tibug项目已上线,大家可以玩一玩:http://123.206.33.109:70 ...
随机推荐
- Quartz 2D编程指南(4) - 颜色和颜色空间
不同的设备(显示器.打印机.扫描仪.摄像头)处理颜色的方式是不同的.每种设备都有其所能支持的颜色值范围.一种设备能支持的颜色可能在其它设备中无法支持.为了有效的使用颜色及理解Quartz 2D中用于颜 ...
- hadoop2.x配合ZooKeeper集群环境搭建
前期准备就不详细说了,课堂上都介绍了1.修改Linux主机名2.修改IP3.修改主机名和IP的映射关系 ######注意######如果你们公司是租用的服务器或是使用的云主机(如华为用主机.阿里云主机 ...
- Appium定位 5 ——xpath
今天我们先来和大家说一下appium,上一次说到了定位,不知道大家定位的怎么样了,今天来说如何定位xpath,五分钟即可学会: 例1: 现在我想定位下面这个登录按钮: xpath该怎么写呢? 先不管三 ...
- sublime自动格式化代码插件HTML-CSS-JS Prettify安装
sublime自动格式化代码插件HTML-CSS-JS Prettify安装 问题: 用 Sublime Text 格式化代码(安装 HTML-CSS-JS Prettify 插件)时,格式化时却会提 ...
- Numpy, Pandas, Matplotlib, Scipy 初步
Numpy: 计算基础, 以类似于matlab的矩阵计算为基础. 底层以C实现, 速度快. Pandas: 以numpy为基础, 扩充了很多统计工具. 重点是数据统计分析. Matplotlib: ...
- 40+个对初学者非常有用的PHP技巧
1.不要使用相对路径,要定义一个根路径 这样的代码行很常见: require_once('../../lib/some_class.php'); 这种方法有很多缺点: 它首先搜索php包括路径中的指定 ...
- 什么是虚拟环境、为什么使用虚拟环境、Anaconda创建、激活、退出、删除虚拟环境
一.虚拟环境 virtual environment 它是一个虚拟化,从电脑独立开辟出来的环境.通俗的来讲,虚拟环境就是借助虚拟机docker来把一部分内容独立出来,我们把这部分独立出来的东西称作“容 ...
- quick 状态机StateMachine
function Player:addStateMachine() self.fsm_ = {} cc.GameObject.extend(self.fsm_) :addComponent(" ...
- c# winform委托的使用
可参考http://bbs.csdn.net/topics/390377875中使用new Action<>的使用方式,替代delegate的申明. public delegate voi ...
- Java语言与C语言混合编程(2)--在Java中调用C语言本地库
在上一篇文章中介绍了Java语言中的native关键字,以及Java语言调用C语言的编译生成本地动态链接库(DLL)实现加法运算的小例子,本文通过一个更加详细的例子,深入讲解Java语言调用C语言的函 ...