基于AOP操作日志

在日常开发中经常需要在代码中加入一些记录用户操作日志的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.     
  3. import  java.lang.annotation.Documented;    
  4. import  java.lang.annotation.ElementType;    
  5. import  java.lang.annotation.Retention;    
  6. import  java.lang.annotation.RetentionPolicy;    
  7. import  java.lang.annotation.Target;    
  8.     
  9.     
  10. @Target ({ElementType.METHOD})    
  11. @Retention (RetentionPolicy.RUNTIME)    
  12. @Documented     
  13. public   @interface  UserOperateLog {    
  14.     /**   
  15.      * 用户操作名称   
  16.      * @return 用户操作名称,默认为空串   
  17.      */     
  18.     String value() default   "" ;    
  19.         
  20.     /**   
  21.      * 用户操作类型,默认类型为0<br/>   
  22.      * 0 - 其他操作 <br/>   
  23.      * 1 - 查询 <br/>   
  24.      * 2 - 新增 <br/>   
  25.      * 3 - 修改 <br/>   
  26.      * 4 - 删除   
  27.      * @return 用户操作类型   
  28.      */     
  29.     int  type()  default   0 ;    
  30.         
  31.     /**   
  32.      * 用户操作名称对应的key,可以通过该key值在属性文件中查找对应的value   
  33.      * @return key   
  34.      */     
  35.     String key() default   "" ;    
  36. }    

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

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

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

为了使通知起作用,需要在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.     
  9. <!-- 定义日志文件写入位置,需要在log4j.properties中加入名称为 useroperatorlog的日志配置-->    
  10. <bean id="userOperateLogAdvisor"   class = "com.hqf.common.annotation.UserOperateLogAdvisor"     
  11.     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.                 
  5.         return   "demo/list" ;    
  6.     
  7.     }    

日志输出结果如下:

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

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

详细的代码请参考附件。

原帖在:点击查看原帖

另一种形式的AOP操作Log

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值