基於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

發佈了30 篇原創文章 · 獲贊 10 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章