基于AOP操作日志
来源:互联网 发布:微信收款盒子对接端口 编辑:程序博客网 时间:2024/05/29 18:38
在日常开发中经常需要在代码中加入一些记录用户操作日志的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 "";
- }
- 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;
- }
- }
- 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"/>
- <!-- 定义用户操作日志切入点和通知器 -->
- <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";
- }
- @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值。
详细的代码请参考附件。
原帖在:点击查看原帖
另一种形式的AOP操作Log
- 基于AOP操作日志
- 基于AOP操作日志
- 基于aop的用户操作日志
- 基于Spring的AOP实现自定义annotation操作日志
- 基于Spring的AOP实现自定义annotation操作日志
- 基于注解的aop实现用户操作日志管理
- 基于AOP的日志实现!
- 基于AOP的日志调试
- 基于aspectj的aop操作
- spring mvc 基于aop日志管理
- aop+自定义注解实现操作日志记录
- spring AOP的 操作日志记录功能
- aop+自定义注解实现操作日志记录
- 使用sping AOP 操作日志管理
- spring的AOP实现记录操作日志
- Spring aop切面做日志处理操作
- Spring AOP 自定义注解记录操作日志
- Spring AOP 实现系统操作日志记录
- fullCalendar日历控件的使用总结
- 网站日常维护方法
- OpenFire源码学习之九:OF的缓存机制
- HDU 2822 Dogs
- Qt--时钟插件的创建与使用
- 基于AOP操作日志
- javascript 原型模式的工作原理 到 对象模式的探寻(下)
- <赖恒发> 赖导说爱上 一个程序员
- 在C++中实现foreach循环
- 【观点】风雨20年:我所积累的20条编程经验
- Stanford机器学习---第八讲. 支持向量机SVM
- [python][爬虫]从网页中下载图片
- MVC的概念
- RTSP协议学习笔记