很多数据库都有操作日志这张表,所以,在访问方法以后自动将操作日志放入数据库是非常重要的一点:
面向切面编程 AOP:其实就是切面与切入点,下文描述了如果设置简单的切面:
首先,自定义注解:
自定义注解里的字段最好包含数据库,操作日志表里的字段。
然后定义切面:
package com.qcby.demo0816.aspect;
import com.qcby.demo0816.anno.SelfLog;
import com.qcby.demo0816.common.context.QcbyContext;
import com.qcby.demo0816.entity.OperateLog;
import com.qcby.demo0816.util.JwtUtil;
import com.qcby.demo0816.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* @className: SelfLogApsect
* @description: 操作日志切面
* @author: thx
* @create: 2021-09-09
**/
@Aspect
@Component
@Slf4j
public class SelfLogApsect {
// request 用于获取 token
@Autowired
private HttpServletRequest httpServletRequest;
/**
* 定义切入点,就是刚刚定义的 注解,只要方法有注解,就切入
*/
@Pointcut("@annotation(com.qcby.demo0816.anno.SelfLog)")
public void selfLogPonitCut(){
}
/**
* 前置通知
* @param joinPoint
*/
@Before("selfLogPonitCut()")
public void testBefore(JoinPoint joinPoint){
log.info("我是一个前置通知");
}
@After("selfLogPonitCut()")
public void after(){
log.info("After................通知");
}
@AfterReturning("selfLogPonitCut()")
public void afterReturning(){
log.info("AfterReturning................通知");
}
@AfterThrowing("selfLogPonitCut()")
public void afterThrowing(){
log.info("AfterThrowing................通知");
}
/**
* 可控制目标函数是否执行
*/
@Around("selfLogPonitCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Around................通知");
log.info("进入Around通知....");
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
String token = httpServletRequest.getHeader("token");
log.info("请求令牌:token:{}",token);
Long userId = null;
if(!StringUtil.isEmpty(token)){
String audience = JwtUtil.getAudience(token);
if(!StringUtil.isEmpty(audience)){
// 获取到用户id
userId = Long.valueOf(audience);
}
}
//获取注解信息
SelfLog selfLog = method.getAnnotation(SelfLog.class);
log.info("selfLog:{}",selfLog);
OperateLog operateLog = new OperateLog();
operateLog.setCreateTime(LocalDateTime.now());
operateLog.setType(selfLog.type());
operateLog.setModule(selfLog.module());
operateLog.setUserId(userId);
// 操作日志入库
log.info("操作日志:operateLog=>{}",operateLog);
String nameValue = selfLog.name();
log.info("nameValue的值为:{}",nameValue);
String name = null;
//获取切入点方法参数
Object[] objects = joinPoint.getArgs();
String[] paramNames = signature.getParameterNames();
for (int i =0;i<paramNames.length;i++){
if (Objects.equals(nameValue,paramNames[i]) && objects[i] != null){
name = objects[i].toString();
}
}
log.info("name的值为:{}",name);
log.info("model的类型:{}",operateLog.getModule());
/**
* 这是目标方法执行的代码
*/
Object r = joinPoint.proceed();
log.info("结束Around通知....");
return r;
}
}
此时只需要将 OperateLog 对象放入数据库即可,完成 每次访问功能,自动入库
完成自动获取操作日志:注:代码不需要掌握,只需理解什么意思即可