1.定義日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
//普通的操作說明
String value() default "";
//spel表達式的操作說明
String spelValue() default "";
}
2.定義spel解析工具類
public class SpelUtil {
/**
* 用于SpEL表達式解析.
*/
private static SpelExpressionParser parser = new SpelExpressionParser();
/**
* 用于擷取方法參數定義名字.
*/
private static DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
public static String generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint) {
// 通過joinPoint擷取被注解方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 使用spring的DefaultParameterNameDiscoverer擷取方法形參名數組
String[] paramNames = nameDiscoverer.getParameterNames(method);
// 解析過後的Spring表達式對象
Expression expression = parser.parseExpression(spELString);
// spring的表達式上下文對象
EvaluationContext context = new StandardEvaluationContext();
// 通過joinPoint擷取被注解方法的形參
Object[] args = joinPoint.getArgs();
// 給上下文指派
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
// 表達式從上下文中計算出實際參數值
/*如:
@annotation(key="#student.name")
method(Student student)
那麼就可以解析出方法形參的某屬性值,return “xiaoming”;
*/
return expression.getValue(context).toString();
}
}
3.定義切面類
@Aspect
@Component
public class SysLogAspect {
@Autowired
private LogService logService;
@Autowired
private HttpServletRequest request;
@Pointcut("@annotation(com.ztri.common.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//執行方法
Object result = point.proceed();
//執行時長(毫秒)
long time = System.currentTimeMillis() - beginTime;
//儲存日志
saveSysLog(point, time);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Log sysLog = new Log();
sysLog.setTime(time);
SysLog syslog = method.getAnnotation(SysLog.class);
if (syslog != null) {
//注解上的描述
if (StrUtil.isNotBlank(syslog.value())) {
sysLog.setOperation(syslog.value());
}
if (StrUtil.isNotBlank(syslog.spelValue())) {
String spelValue = SpelUtil.generateKeyBySpEL(syslog.spelValue(), joinPoint);
sysLog.setOperation(spelValue);
}
}
//請求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
//請求的參數
Object[] args = joinPoint.getArgs();
try {
String params = JSONUtil.toJsonStr(args);
sysLog.setParams(params);
} catch (Exception e) {
}
//設定IP位址
sysLog.setIp(ServletUtil.getClientIP(request));
UserAgent ua = UserAgentUtil.parse(request.getHeader("User-Agent"));
sysLog.setBrowser(ua.getBrowser().toString());
//儲存系統日志
logService.create(sysLog);
}
}
4.方法上使用日志注解
@ApiOperation("進階搜尋(包含點選1.熱門清單 2.更多跳轉頁面)")
@PostMapping("searchData")
@SysLog(spelValue = "'進階搜尋' + #searchVo.keyWord")
public ResponseEntity<Object> searchData(@RequestBody SearchVo searchVo) throws IOException {
SearchDto searchDto = searchService.searchData(searchVo);
return new ResponseEntity<>(searchDto, HttpStatus.OK);
}
@ApiOperation("登入授權")
@PostMapping("/login")
@SysLog("使用者登入")
public ResponseEntity<Object> login(@Validated(User.Create.class) @RequestBody LoginUser loginUser) {
return ResponseEntity.ok(authInfo);
}