運用切面實現使用者行為日誌的新增
-
Dao介面實現
- 業務描述與設計實現
資料層基於業務層的持久化請求,將業務層提交的使用者行為日誌資訊寫入到資料庫。
- 關鍵程式碼設計與實現
在SysLogDao介面中新增用於實現日誌資訊持久化的方法。關鍵程式碼如下:
void saveObject(SysLog entity)
第二步:在SysLogServiceImpl類中新增,儲存日誌的方法實現。關鍵程式碼如下:
@Override
public void saveObject(SysLog entity) {
sysLogDao.insertObject(entity);
}
-
日誌切面Aspect實現
- 業務描述與設計實現
在日誌切面中,抓取使用者行為資訊,並將其封裝到日誌物件然後傳遞到業務,通過業務層物件對日誌日誌資訊做進一步處理。此部分內容後續結合AOP進行實現(暫時先了解,不做具體實現)。
- 關鍵程式碼設計與實現
定義日誌切面類物件,通過環繞通知處理日誌記錄操作。關鍵程式碼如下:
package com.cy.pj.common.aspect;
import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.common.util.IPUtils;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.pojo.SysUser;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Date;
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
public void doLog(){}
@Around("doLog()")
public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable{
long t1=System.currentTimeMillis();
Object result=joinPoint.proceed();
long t2=System.currentTimeMillis();
doSaveLog(joinPoint,(t2-t1));
return result;//目標方法的執行結果
}
/**記錄使用者正常行為日誌:
* username (登入使用者)
* ip (通過工具類獲取)
* operation (一般是通過註解指定或定義)
* method (目標型別的類全名+方法名)
* params(執行目標方法時傳入的引數)
* time (執行目標方法時的耗時時長)
* createdtime (日誌的記錄時間)
* */
private void doSaveLog(ProceedingJoinPoint jointPoint,long time) throws NoSuchMethodException, JsonProcessingException {
//想要獲取目標物件的操作內容,得先獲取目標物件的的型別,基於型別獲取目標方法,
//1.獲取使用者行為日誌,IPUtils是一個獲取ip地址工具類,需要外界匯入
String ip= IPUtils.getIpAddr();
//2.獲取目標物件型別(為什麼要獲取此型別呢?要基於此型別獲取目標方法)
Class<?> targetCls=jointPoint.getTarget().getClass();
System.out.println("targetCls="+targetCls.getName());
//3獲取目標方法,獲取目標方法會攜帶方法名和引數型別,所以我們要先獲取一個方法簽名
//3.1獲取方法簽名(儲存了方法資訊的一個物件),通過此簽名獲取目標方法的名字name以及引數型別ParameterTypes
MethodSignature ms=(MethodSignature) jointPoint.getSignature();
//3.2DeclaredMethod獲取私有的方法
Method targetMethod= targetCls.getDeclaredMethod(ms.getName(),ms.getParameterTypes());
//4.獲取目標方法上的requiredLog註解
RequiredLog requiredLog=targetMethod.getAnnotation(RequiredLog.class);
//5.獲取註解中operation屬性的值
String operation=requiredLog.operation();;//操作名
//6.目標方法(目標型別的類全名+方法名)
String method=targetCls.getName()+"."+targetMethod.getName();
//7.(執行目標方法時傳入的實際引數值),拿出來的引數是一個陣列,需要轉換成字串,
// 如果傳入的是pojo,會自動轉成json字串格式
String params= new ObjectMapper().writeValueAsString(jointPoint.getArgs());
//8.封裝使用者行為日誌
SysLog log=new SysLog();
SysUser user= (SysUser)SecurityUtils.getSubject().getPrincipal();
log.setUsername(user.getUsername());
log.setIp(ip);
log.setOperation(operation);
log.setMethod(method);
log.setParams(params);
log.setTime(time);
log.setCreatedTime(new Date());
//3.儲存使用者行為日誌
sysLogService.saveObject(log);
}
}
方法中用到的ip地址獲取需要提供一個如下的工具類:(不用自己實現,直接用)
package com.cy.pj.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class IPUtils {
private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
public static String getIpAddr() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
logger.error("IPUtils ERROR ", e);
}
return ip;
}
}
定義註解介面的實現,通過此註解作為切面的切入點
package com.cy.pj.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
String operation() default "";
}
例如在實現類中加註解實現:
@RequiredLog(operation = "禁用啟用")//此註解描述的方法為日誌切入點方法
@RequiresPermissions("sys:user:update")//註解中的字串為一個許可權標識
@Override
public int validById(Integer id, Integer valid) {
//1.引數校驗
//2.修改使用者狀態
int rows=sysUserDao.validById(id,valid,"admin");//這裡的admin暫時理解為登入使用者
if(rows==0)
throw new ServiceException("記錄有可能已經不存在");
return rows;
}
相關文章
- Ubuntu-給新增使用者新增root許可權
- Java多執行緒技術:實現多使用者服務端Socket通訊
- python實現淘寶使用者行為分析
- Linux入門_基礎命令_使用者和組相關命令
- win10修改使用者名稱/指紋無法置入/使用者檔案沒有重新命名的選項
- macos下parallel Desktop 中centos忘記密碼及修改原使用者密碼
- [Docker]在Ubuntu容器中建立sudo使用者
- oracle資料庫使用者建立步驟
- 本地oracle資料庫忘記使用者名稱密碼解決方案
- 2020休閒射擊遊戲報告:市場使用者風格玩法選擇建議與爆款資料案例分析
- python之矩陣相加:提示使用者輸入矩陣的行數n,再提示使用者輸入矩陣的列數m,接下來,提示使用者輸入 2*n*m 個數字(每次輸入 一個數字)。輸出 C=A+B。
- 表單運用和基礎練習
- B站:3Q20財報電話會議實錄 未來使用者需要的產品一定是覆蓋全場景 多品類
- Sensor Tower:15%的美國iPhone使用者安裝了自定義iOS 14主螢幕小部件的應用