背景:我接到一个需求要记录账号的操作记录比如那个用户添加了什么数据修改了什么数据删除了什么数据,起初我的想法是在每个方法上加一段代码我也是这么做的我只要把用户信息还有前端发来的参数以及后端要返回的参数以及当前时间记录下来就好了然后我修改了几个方法我突然想起来要知道用户是什么操作对于那个模块的操作然后我来修改我发现修改起来太麻烦了每一个方法都要去修改几乎没有可维护性于是我想给他封装一下可是每个方法里加的代码无非就是new一个实体类然后把参数放进去然后掉一下service实在没有可以封装的地方这个时候我想起来非常强大的面向百度编程去搜了一下说是自定义注解面向切面编程一开始我百度出来有十几个类然后我一点点的虑最后是我总结了一下除了service和mapper来保存数据库(保存日志文件也可以)一共需要四个类一个类是自定义注解类
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyLog {
/**
* Title 模块
* */
Module module() default Module.GOODS;
/**
* 操作
* */
Manipulate manipulate() default Manipulate.QUERY;
}
这里定义的两个方法是使用这个注解需要的参数default后面的是默认参数我这里是用的java8新特性枚举类
然后还需要一个类似于service实现类的类
import org.aspectj.lang.JoinPoint;
import cn.hutool.json.JSONUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;
/**
* MyLog 实现 Aop 切面类
*/
@Aspect
@Component
public class LogAspect {
@Resource
private LogService sysLogService;
ThreadLocal<Long> currentTime = new ThreadLocal<>();
/**
* 基 础 上 下 文
*/
/**
* 配置切入点(切 面 编 程)
*/
@Pointcut("@annotation(com.yunli.agent.log.MyLog) || @within(com.yunli.agent.log.MyLog)")
public void logPointcut() {
// 该方法无方法体,主要为了让同类中其他方法使用此切入点
}
/**
* 处 理 系 统 日 志(配置环绕通知,使用在方法logPointcut()上注册的切入点)
*
* @param joinPoint join point for advice
*/
@Around("logPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String userId = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
.getRequest()
.getHeader("userId");
HttpServletRequest request=((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
.getRequest();
// 记录方法的执行时间
currentTime.set(System.currentTimeMillis());
// 执 行 方 法
Object result = null;
result = joinPoint.proceed();
//返回参数
String resultData= JSON.toJSONString(result);
// 注解解析
MyLog annotation = getAnnotation(joinPoint);
Manipulate manipulate = annotation.manipulate();
Module module = annotation.module();
// 获取请求参数
String requestData = getParameterToJson( joinPoint);
// 请求耗时
Long time = System.currentTimeMillis() - currentTime.get();
currentTime.remove();
//
sysLogService.saveLog(Integer.valueOf(userId), module, manipulate, requestData, resultData);
return result;
}
/**
* 配置异常通知
*
* @param joinPoint join point for advice
* @param e exception
*/
@AfterThrowing(pointcut = "logPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
String userId = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
.getRequest()
.getHeader("userId");
HttpServletRequest request=((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
.getRequest();
// 注解解析
MyLog annotation = getAnnotation((ProceedingJoinPoint) joinPoint);
Module module = annotation.module();
Manipulate manipulate = annotation.manipulate();
// 获取参数
String requestData = getParameterToJson((ProceedingJoinPoint) joinPoint);
// 请求耗时
Long time = System.currentTimeMillis() - currentTime.get();
currentTime.remove();
// 记 录 日 志
sysLogService.saveLog(Integer.valueOf(userId), module, manipulate, requestData, e.toString());
}
/**
* 获 取 注 解
*/
public MyLog getAnnotation(ProceedingJoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
Class<? extends Object> targetClass = point.getTarget().getClass();
MyLog targetLog = targetClass.getAnnotation(MyLog.class);
if (targetLog != null) {
return targetLog;
} else {
Method method = signature.getMethod();
MyLog log = method.getAnnotation(MyLog.class);
return log;
}
}
/**
* 获 取 参数(转换json格式)
*/
public String getParameterToJson(ProceedingJoinPoint point) {
List<Object> argList = new ArrayList<>();
//参数值
Object[] argValues = point.getArgs();
//参数名称
String[] argNames = ((MethodSignature)point.getSignature()).getParameterNames();
if(argValues != null){
for (int i = 0; i < argValues.length; i++) {
Map<String, Object> map = new HashMap<>();
String key = argNames[i];
map.put(key, argValues[i]);
argList.add(map);
map = null;
}
}
if (argList.size() == 0) {
return "";
}
return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : JSONUtil.toJsonStr(argList);
}
}
module是模块比如说对于商品的操作对于分类的操作对于品牌的分类等等
manipulate是操作比如说删除,添加,修改
userId表示用户信息
requestData表示前端发送请求参数
resultData表示返回到前端的参数可以知道操作是否成功以及失败原因
logAfterThrowing这个方法是当执行发生异常的时候执行的可以知道这个操作发生了什么异常看需要是否需要这个方法
这样就完成了面向切面编程实现基本操作日志功能