springAOP自定義註解方式實現日誌管理

今天繼續實現AOP,到這裏我我的認爲是最靈活,可擴展的方式了,就拿日誌管理來講,用SPRing AOP 自定義註解形式實現日誌管理。廢話很少說,直接開始!!! html

關於配置我仍是的再說一遍。 java

 

applicationContext-mvc.xml中要添加的 web

     <mvc:annotation-driven />
     <!-- 激活組件掃描功能,在包com.gcx及其子包下面自動掃描經過註解配置的組件 -->
     <context:component-scan base-package="com.gcx" /> spring

  

     <!-- 啓動對@aspectJ註解的支持 --> 
     <!-- proxy-target-class等於true是強制使用cglib代理,proxy-target-class默認是false,若是你的類實現了接口 就走JDK代理,若是沒有,走cglib代理  -->
     <!-- 注:對於單利模式建議使用cglib代理,雖然JDK動態代理比cglib代理速度快,但性能不如cglib --> sql

     <!--若是不寫proxy-target-class="true"這句話也沒問題-->
     <aop:aspectj-autoproxy proxy-target-class="true"/> 數據庫

     <!--切面-->
     <bean id="systemLogAspect" class="com.gcx.annotation.SystemLogAspect"></bean> session

接下來開始編寫代碼。 mvc

     建立日誌類實體 app

 1 public class SystemLog {  2 private String id;  3  4 private String description;  5  6 private String method;  7  8 private Long logType;  9  10 private String requestIp;  11  12 private String exceptioncode;  13  14 private String exceptionDetail;  15  16 private String params;  17  18 private String createBy;  19  20 private Date createDate;  21  22 public String getId() {  23 return id;  24  }  25  26 public void setId(String id) {  27 this.id = id == null ? null : id.trim();  28  }  29  30 public String getDescription() {  31 return description;  32  }  33  34 public void setDescription(String description) {  35 this.description = description == null ? null : description.trim();  36  }  37  38 public String getMethod() {  39 return method;  40  }  41  42 public void setMethod(String method) {  43 this.method = method == null ? null : method.trim();  44  }  45  46 public Long getLogType() {  47 return logType;  48  }  49  50 public void setLogType(Long logType) {  51 this.logType = logType;  52  }  53  54 public String getRequestIp() {  55 return requestIp;  56  }  57  58 public void setRequestIp(String requestIp) {  59 this.requestIp = requestIp == null ? null : requestIp.trim();  60  }  61  62 public String getExceptioncode() {  63 return exceptioncode;  64  }  65  66 public void setExceptioncode(String exceptioncode) {  67 this.exceptioncode = exceptioncode == null ? null : exceptioncode.trim();  68  }  69  70 public String getExceptionDetail() {  71 return exceptionDetail;  72  }  73  74 public void setExceptionDetail(String exceptionDetail) {  75 this.exceptionDetail = exceptionDetail == null ? null : exceptionDetail.trim();  76  }  77  78 public String getParams() {  79 return params;  80  }  81  82 public void setParams(String params) {  83 this.params = params == null ? null : params.trim();  84  }  85  86 public String getCreateBy() {  87 return createBy;  88  }  89  90 public void setCreateBy(String createBy) {  91 this.createBy = createBy == null ? null : createBy.trim();  92  }  93  94 public Date getCreateDate() {  95 return createDate;  96  }  97  98 public void setCreateDate(Date createDate) {  99 this.createDate = createDate; 100  } 101 }
View Code

     編寫dao接口 dom

 1 package com.gcx.dao;  2  3 import com.gcx.entity.SystemLog;  4  5 public interface SystemLogMapper {  6 int deleteByPrimaryKey(String id);  7  8 int insert(SystemLog record);  9 10 int insertSelective(SystemLog record); 11 12  SystemLog selectByPrimaryKey(String id); 13 14 int updateByPrimaryKeySelective(SystemLog record); 15 16 int updateByPrimaryKey(SystemLog record); 17 }
View Code

    編寫service層

 1 package com.gcx.service;  2  3 import com.gcx.entity.SystemLog;  4  5 public interface SystemLogService {  6  7 int deleteSystemLog(String id);  8  9 int insert(SystemLog record); 10 11 int insertTest(SystemLog record); 12 13  SystemLog selectSystemLog(String id); 14 15 int updateSystemLog(SystemLog record); 16 }
View Code

   編寫service實現類serviceImpl

 1 package com.gcx.service.impl;  2  3 import javax.annotation.Resource;  4  5 import org.springframework.stereotype.Service;  6  7 import com.gcx.annotation.Log;  8 import com.gcx.dao.SystemLogMapper;  9 import com.gcx.entity.SystemLog; 10 import com.gcx.service.SystemLogService; 11 12 @Service("systemLogService") 13 public class SystemLogServiceImpl implements SystemLogService { 14 15  @Resource 16 private SystemLogMapper systemLogMapper; 17 18  @Override 19 public int deleteSystemLog(String id) { 20 21 return systemLogMapper.deleteByPrimaryKey(id); 22  } 23 24  @Override 25 26 public int insert(SystemLog record) { 27 28 return systemLogMapper.insertSelective(record); 29  } 30 31  @Override 32 public SystemLog selectSystemLog(String id) { 33 34 return systemLogMapper.selectByPrimaryKey(id); 35  } 36 37  @Override 38 public int updateSystemLog(SystemLog record) { 39 40 return systemLogMapper.updateByPrimaryKeySelective(record); 41  } 42 43  @Override 44 public int insertTest(SystemLog record) { 45 46 return systemLogMapper.insert(record); 47  } 48 49 }
View Code

到這裏基本程序編寫完畢

下面開始自定義註解

 1 package com.gcx.annotation;  2  3 import java.lang.annotation.*;  4  5 @Target({ElementType.PARAMETER, ElementType.METHOD})  6 @Retention(RetentionPolicy.RUNTIME)  7 @Documented  8 public @interface Log {  9 10 /** 要執行的操做類型好比:add操做 **/ 11 public String OperationType() default ""; 12 13 /** 要執行的具體操做好比:添加用戶 **/ 14 public String operationName() default ""; 15 }

 

下面編寫切面

 1 package com.gcx.annotation;  2  3 import java.lang.reflect.Method;  4 import java.util.Date;  5 import java.util.UUID;  6  7 import javax.annotation.Resource;  8 import javax.servlet.http.HttpServletRequest;  9 import javax.servlet.http.Httpsession;  10  11 import org.aspectj.lang.JoinPoint;  12 import org.aspectj.lang.ProceedingJoinPoint;  13 import org.aspectj.lang.annotation.After;  14 import org.aspectj.lang.annotation.AfterReturning;  15 import org.aspectj.lang.annotation.AfterThrowing;  16 import org.aspectj.lang.annotation.Around;  17 import org.aspectj.lang.annotation.Aspect;  18 import org.aspectj.lang.annotation.Before;  19 import org.aspectj.lang.annotation.Pointcut;  20 import org.slf4j.Logger;  21 import org.slf4j.LoggerFactory;  22 import org.springframework.stereotype.Component;  23  24 import com.gcx.entity.SystemLog;  25 import com.gcx.entity.User;  26 import com.gcx.service.SystemLogService;  27 import com.gcx.util.JsonUtil;  28  29 /**  30  * @author 楊建  31  * @E-mail: email  32  * @version 建立時間:2015-10-19 下午4:29:05  33  * @desc 切點類  34 */  35  36 @Aspect  37 @Component  38 public class SystemLogAspect {  39  40 //注入Service用於把日誌保存數據庫   41 @Resource //這裏我用resource註解,通常用的是@Autowired,他們的區別若有時間我會在後面的博客中來寫  42 private SystemLogService systemLogService;  43  44 private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);  45  46 //Controller層切點   47 @Pointcut("execution (* com.gcx.controller..*.*(..))")  48 public void controllerAspect() {  49  }  50  51 /**  52  * 前置通知 用於攔截Controller層記錄用戶的操做  53  *  54  * @param joinPoint 切點  55 */  56 @Before("controllerAspect()")  57 public void doBefore(JoinPoint joinPoint) {  58 System.out.println("==========執行controller前置通知===============");  59 if(logger.isInfoEnabled()){  60 logger.info("before " + joinPoint);  61  }  62  }  63  64 //配置controller環繞通知,使用在方法aspect()上註冊的切入點  65 @Around("controllerAspect()")  66 public void around(JoinPoint joinPoint){  67 System.out.println("==========開始執行controller環繞通知===============");  68 long start = System.currentTimeMillis();  69 try {  70  ((ProceedingJoinPoint) joinPoint).proceed();  71 long end = System.currentTimeMillis();  72 if(logger.isInfoEnabled()){  73 logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");  74  }  75 System.out.println("==========結束執行controller環繞通知===============");  76 } catch (Throwable e) {  77 long end = System.currentTimeMillis();  78 if(logger.isInfoEnabled()){  79 logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());  80  }  81  }  82  }  83  84 /**  85  * 後置通知 用於攔截Controller層記錄用戶的操做  86  *  87  * @param joinPoint 切點  88 */  89 @After("controllerAspect()")  90 public void after(JoinPoint joinPoint) {  91  92 /* HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  93  HttpSession session = request.getSession(); */  94 //讀取session中的用戶  95 // User user = (User) session.getAttribute("user");  96 //請求的IP  97 //String ip = request.getRemoteAddr();  98 User user = new User();  99 user.setId(1); 100 user.setName("張三"); 101 String ip = "127.0.0.1"; 102 try { 103 104 String targetName = joinPoint.getTarget().getClass().getName(); 105 String methodName = joinPoint.getSignature().getName(); 106 Object[] arguments = joinPoint.getArgs(); 107 Class targetClass = Class.forName(targetName); 108 Method[] methods = targetClass.getMethods(); 109 String operationType = ""; 110 String operationName = ""; 111 for (Method method : methods) { 112 if (method.getName().equals(methodName)) { 113 Class[] clazzs = method.getParameterTypes(); 114 if (clazzs.length == arguments.length) { 115 operationType = method.getAnnotation(Log.class).operationType(); 116 operationName = method.getAnnotation(Log.class).operationName(); 117 break; 118  } 119  } 120  } 121 //*========控制檯輸出=========*// 122 System.out.println("=====controller後置通知開始====="); 123 System.out.println("請求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); 124 System.out.println("方法描述:" + operationName); 125 System.out.println("請求人:" + user.getName()); 126 System.out.println("請求IP:" + ip); 127 //*========數據庫日誌=========*// 128 SystemLog log = new SystemLog(); 129  log.setId(UUID.randomUUID().toString()); 130  log.setDescription(operationName); 131 log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); 132 log.setLogType((long)0); 133  log.setRequestIp(ip); 134 log.setExceptioncode( null); 135 log.setExceptionDetail( null); 136 log.setParams( null); 137  log.setCreateBy(user.getName()); 138 log.setCreateDate(new Date()); 139 //保存數據庫  140  systemLogService.insert(log); 141 System.out.println("=====controller後置通知結束====="); 142 } catch (Exception e) { 143 //記錄本地異常日誌  144 logger.error("==後置通知異常=="); 145 logger.error("異常信息:{}", e.getMessage()); 146  } 147  } 148 149 //配置後置返回通知,使用在方法aspect()上註冊的切入點 150 @AfterReturning("controllerAspect()") 151 public void afterReturn(JoinPoint joinPoint){ 152 System.out.println("=====執行controller後置返回通知====="); 153 if(logger.isInfoEnabled()){ 154 logger.info("afterReturn " + joinPoint); 155  } 156  } 157 158 /** 159  * 異常通知 用於攔截記錄異常日誌 160  * 161  * @param joinPoint 162  * @param e 163 */ 164 @AfterThrowing(pointcut = "controllerAspect()", throwing="e") 165 public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { 166 /*HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 167  HttpSession session = request.getSession(); 168  //讀取session中的用戶 169  User user = (User) session.getAttribute(WebConstants.CURRENT_USER); 170  //獲取請求ip 171  String ip = request.getRemoteAddr(); */ 172 //獲取用戶請求方法的參數並序列化爲JSON格式字符串  173 174 User user = new User(); 175 user.setId(1); 176 user.setName("張三"); 177 String ip = "127.0.0.1"; 178 179 String params = ""; 180 if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) { 181 for ( int i = 0; i < joinPoint.getArgs().length; i++) { 182 params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";"; 183  } 184  } 185 try { 186 187 String targetName = joinPoint.getTarget().getClass().getName(); 188 String methodName = joinPoint.getSignature().getName(); 189 Object[] arguments = joinPoint.getArgs(); 190 Class targetClass = Class.forName(targetName); 191 Method[] methods = targetClass.getMethods(); 192 String operationType = ""; 193 String operationName = ""; 194 for (Method method : methods) { 195 if (method.getName().equals(methodName)) { 196 Class[] clazzs = method.getParameterTypes(); 197 if (clazzs.length == arguments.length) { 198 operationType = method.getAnnotation(Log.class).operationType(); 199 operationName = method.getAnnotation(Log.class).operationName(); 200 break; 201  } 202  } 203  } 204 /*========控制檯輸出=========*/ 205 System.out.println("=====異常通知開始====="); 206 System.out.println("異常代碼:" + e.getClass().getName()); 207 System.out.println("異常信息:" + e.getMessage()); 208 System.out.println("異常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); 209 System.out.println("方法描述:" + operationName); 210 System.out.println("請求人:" + user.getName()); 211 System.out.println("請求IP:" + ip); 212 System.out.println("請求參數:" + params); 213 /*==========數據庫日誌=========*/ 214 SystemLog log = new SystemLog(); 215  log.setId(UUID.randomUUID().toString()); 216  log.setDescription(operationName); 217  log.setExceptioncode(e.getClass().getName()); 218 log.setLogType((long)1); 219  log.setExceptionDetail(e.getMessage()); 220 log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); 221  log.setParams(params); 222  log.setCreateBy(user.getName()); 223 log.setCreateDate(new Date()); 224  log.setRequestIp(ip); 225 //保存數據庫  226  systemLogService.insert(log); 227 System.out.println("=====異常通知結束====="); 228 } catch (Exception ex) { 229 //記錄本地異常日誌  230 logger.error("==異常通知異常=="); 231 logger.error("異常信息:{}", ex.getMessage()); 232  } 233 /*==========記錄本地異常日誌==========*/ 234 logger.error("異常方法:{}異常代碼:{}異常信息:{}參數:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params); 235 236  } 237 238 }

 我這裏寫的比較全,前置通知,環繞通知,後置通知,異常通知,後置飯後通知,都寫上了,在咱們實際編寫中不寫全也沒事,我習慣上把記錄日誌的邏輯寫在後置通知裏面,我看網上也有些在前置通知裏面的,但我感受寫在後置通知裏比較好。

下面開始在controller中加入自定義的註解!!

 1 package com.gcx.controller;  2  3 import org.springframework.beans.factory.annotation.Autowired;  4 import org.springframework.stereotype.Controller;  5 import org.springframework.web.bind.annotation.RequestMapping;  6  7 import com.gcx.annotation.Log;  8 import com.gcx.service.UserService;  9 10 @Controller 11 @RequestMapping("userController") 12 public class UserController { 13 14  @Autowired 15 private UserService userService; 16 17 @RequestMapping("testAOP") 18 @Log(operationType="add操做:",operationName="添加用戶") 19 public void testAOP(String userName,String passWord){ 20  userService.addUser(userName, password); 21  } 22 }

下面編寫測試類

1 @Test 2 public void testAOP1(){ 3 //啓動Spring容器  4 ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-mvc.xml","classpath:applicationContext-dataSource.xml"}); 5 //獲取service或controller組件 6 UserController userController = (UserController) ctx.getBean("userController"); 7 userController.testAOP("zhangsan", "123456"); 8  } 9

數據庫數據:

我本來想寫兩個切點,一個是service層,一個是controller層,service層是用來記錄異常信息的日誌,而controller層的是用來記錄功能的日誌,運行結果以下。     

這樣作的話不知道在實際的項目中運行效率好很差,在這裏請看到博客的大牛給點建議!!

相關文章
相關標籤/搜索