一、自定义操作日志注解类
import java.lang.annotation.*;
@Target(ElementType.METHOD)//注解放置的目标位置即方法级别
@Retention(RetentionPolicy.RUNTIME)//注解在哪个阶段执行
@Documented
public @interface OperateLogAnnotation {
String operateModel() default ""; // 操作模块,比如:用户模块 - 登录
String operateType() default ""; // 操作类型 新增、删除等
String remark() default ""; // 操作说明
}
二、操作日志记录实现类
import com.xxx.OperateLogAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
@Aspect
@Component
@Slf4j
public class OperationLogAspect {
/**
* @Pointcut 定义切点表达式
* 设置操作日志切入点 在注解的位置切入代码
*/
@Pointcut("@annotation(com.xxx.OperateLogAnnotation)")
public void operLogPoinCut() {
//log.info("日志=========================切入点");
}
/**
* @Before 前置通知
* @param joinPoint 连接点:比如接口方法被调用的时候就是日志切面的连接点
*/
@Before(value = "operLogPoinCut()")
public void before(JoinPoint joinPoint) {
//log.info("日志--------在方法执行前(紧邻切入点)执行");
}
/**
* @Around 环绕通知:通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(value = "operLogPoinCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//log.info("日志--------方法执行前后都有通知,通知在方法前还是后取决于其中的 ProceedingJoinPoint.proceed() 的位置;");
long startTime = System.currentTimeMillis();
//获取当前请求对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//记录请求信息
Object[] objs = joinPoint.getArgs();
Object result = joinPoint.proceed();//返回的结果,这是一个进入方法和退出方法的一个分界
Signature signature = joinPoint.getSignature();
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature methodSignature = (MethodSignature) signature;
//获取切入点所在的方法
Method method = methodSignature.getMethod();
long endTime = System.currentTimeMillis();
long millisecond = (endTime-startTime);
try {
//获取操作描述
OperateLogAnnotation annotation = method.getAnnotation(OperateLogAnnotation.class);
//业务逻辑,把操作日志插入数据库中去
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* @AfterReturning 返回通知:在目标方法成功执行之后调用通知功能
* @param result 返回结果
* @throws Throwable
*/
@AfterReturning(value = "operLogPoinCut()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint,Object result) throws Throwable {
}
/**
* 异常通知:方法抛出异常之后通知
* @param joinPoint
* @param ex
*/
@AfterThrowing(pointcut = "operLogPoinCut()", throwing = "ex")
public void doAfterThrowing(JoinPoint joinPoint, Exception ex){
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
log.info("==========日志切面异常通知 {}.{} 异常 {} ==========", className, methodName, ex.getMessage());
}
/**
* 根据方法和传入的参数获取请求参数
*/
private Object getParameter(Method method, Object[] args) {
List<Object> argList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
//将RequestBody注解修饰的参数作为请求参数
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
if (requestBody != null) {
argList.add(args[i]);
}
//将RequestParam注解修饰的参数作为请求参数
RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
if (requestParam != null) {
Map<String, Object> map = new HashMap<>();
String key = parameters[i].getName();
if (!StringUtils.isEmpty(requestParam.value())) {
key = requestParam.value();
}
map.put(key, args[i]);
argList.add(map);
} else {
argList.add(args[i]);
}
}
if (argList.size() == 0) {
return null;
} else if (argList.size() == 1) {
return argList.get(0);
} else {
return argList;
}
}
}
三、操作日志注解放在你要切入的方法上
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/log")
@OperateLogAnnotation(operateModel = "测试模块", operateType = "测试", remark = "测试日志")
public ApiResult log() {
return ApiResult.ok();
}
}