1. 程式人生 > >使用Spring AOP自定義註解方式實現使用者操作日誌記錄

使用Spring AOP自定義註解方式實現使用者操作日誌記錄

1,開發環境
作業系統:Windows 7
JDK:1.8.0_161
Eclipse:Mars.2 Release (4.5.2)

2,自定義註解類UserLog

@Target({ElementType.PARAMETER, ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented
public @interface UserLog {
	
	/** 要執行的操作內容比如:使用者:XXX登入成功 **/
	public String operationContent() default "";
}

3,日誌處理切面UserLogAspect
這裡使用的是後通知,當業務程式碼執行完成後,再執行日誌儲存。

@Aspect
@Component
public class UserLogAspect {

	@Autowired
	private ILogSysUserService logSysUserService;
	
	//Controller層切點  
	@Pointcut("@annotation(com.riskraider.common.annotation.UserLog)")
	public void controllerAspect() {
		
	}
	
	/** 
	 * 前置通知 用於攔截Controller層記錄使用者的操作 
	 * 
	 * @param joinPoint 切點 
	 */ 
	@SuppressWarnings("rawtypes")
	@After("controllerAspect()")
	public void doAfter(JoinPoint joinPoint) {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		
		HttpSession session = request.getSession();
		
		//讀取session中的使用者
		SysAccount account = (SysAccount) session.getAttribute("account");
		
		if(null != account){
			//獲取請求ip
			String ip = request.getRemoteAddr();
			
			try{
				String targetName = joinPoint.getTarget().getClass().getName();
				String methodName = joinPoint.getSignature().getName();
				Object[] arguments = joinPoint.getArgs();
				Class targetClass = Class.forName(targetName);
				Method[] methods = targetClass.getMethods();
				String operationUrl = request.getServletPath();
				String operationContent = "";
				
				for (Method method : methods) {
					if (method.getName().equals(methodName)) {
						Class[] clazzs = method.getParameterTypes();
						
						if (clazzs.length == arguments.length) {
							//凡是加了UserLog註解的方法都記錄日誌
							if(null != method.getAnnotation(UserLog.class)){
								operationContent = method.getAnnotation(UserLog.class).operationContent();
								
								//獲取使用者請求方法的引數並序列化為JSON格式字串    
								Map<String,Object> nameAndArgs = null;
						        
						        if(null != joinPoint.getArgs() && joinPoint.getArgs().length > 0) {
						        	//獲取引數名稱和值  
							        nameAndArgs = getParameMap(joinPoint);
						        }
								
						        SysUserLogDTO log = new SysUserLogDTO();
								log.setUserId(account.getAccountId());
								log.setUserName(account.getName());
								log.setUserIp(ip);
								log.setOperationUrl(operationUrl);
								log.setOperationContent(operationContent);
								log.setParameMap(nameAndArgs);
								
								logSysUserService.saveSysUserLog(log);
								
								break;
							}
						}
					}
				}
			}catch(Exception e) {
				LoggerUtils.error("記錄使用者操作日誌失敗!", e);
			}
		}
	}
	
	/**
	 * @Title: getParameMap
	 * @Description: 獲取引數的名稱和值
	 * @author: 施勇
	 * @date: 2018年10月3日 下午3:12:26
	 * @param: @param joinPoint
	 * @param: @return
	 * @return: Map<String,Object>
	 * @throws
	 */
	public Map<String,Object> getParameMap(JoinPoint joinPoint){
		Map<String, Object> map = new HashMap<String, Object>();
		
		Object[] args = joinPoint.getArgs(); // 引數值
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames(); // 引數名
		
        for(int i=0;i<argNames.length;i++){
        	if("response".equals(argNames[i]) || "request".equals(argNames[i]) || "pwd".equals(argNames[i])){
        		continue;
        	}else{
        		map.put(argNames[i], args[i]);
        	}
        }
        
		return map;
	}
	
}

getParameMap()這個方法是用於獲取使用者請求中的引數,只需要儲存業務相關的引數就可以了,所以將request和response過濾掉了,同時也不儲存使用者登入時的密碼。對這個引數Map在儲存到資料庫中的時候可以轉換成JSONString,這樣方便檢視。

4,使用註解
在需要記錄日誌的地方加上註解

	@RequestMapping("/doLogin")
	@ResponseBody
	@UserLog(operationContent="登入系統")
	public ResultBean doLogin(String loginName, String pwd, HttpServletRequest request,HttpServletResponse response){
	
}

這裡重點是AOP切面儲存使用者操作日誌,其他AOP相關配置就不重複說了。