Springboot项目使用AOP切面对增删改查操作进行记录日志操作
前言
AOP它通过将横切关注点(例如日志记录、事务管理、权限控制等)从主要业务逻辑中分离出来,以模块化的方式进行管理。在AOP中,通过定义切面(Aspect)来捕获和处理横切关注点,然后将其应用于特定的目标对象或方法。
一、AOP是什么?
官方的解释有点抽象,我们举个例子说明:假设我们需要在多个方法中添加日志记录功能。传统的方式是在每个方法中都添加日志代码,但这样会导致代码重复,并且当我们需要修改日志记录逻辑时,需要逐个修改所有方法。而使用AOP,我们只需定义一个切面,将日志记录的逻辑写在切面中。然后,通过在需要添加日志的地方进行配置,就能自动将切面应用到目标方法中,实现日志记录的功能。
二、使用步骤
1.引入库
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.编写一个自己定义的Log注解
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TypeByFileValid {
// 模块名称
String title() default "";
}
3.根据业务添加注解
/**
* 新增
*/
@PostMapping("/addFile")
@TypeByFileValid(title = "上传文件")
public Result addFile(Searcher searcher) {
Map<String, Object> map = xxxxService.saveFile(searcher);
return Result.success(map);
}
/**
* 删除
*/
@PostMapping("/removeFile")
@TypeByFileValid(title = "删除")
public Result removeFile(@RequestBody Map<String, Object> param) {
xxxxService.deleteFile(param);
return Result.success();
}
/**
* 下载/预览 文件
*/
@PostMapping("/download")
@TypeByFileValid(title = "下载")
public Result download(@RequestBody JSONObject param) {
return Result.success(xxxxxService.download(param.getString("path")));
}
4.定义切面
@Slf4j
@Aspect
@Component
public class FileOperationLogAspect implements Ordered {
/**
* 在业务代码执行过程中,业务中如果失败了可能会出现事务回滚,导致失败日志也无法插入数据库中。
* 所以这里实现Ordered类,改变业务类处理优先级问题
* @return
*/
@Override
public int getOrder() {
return 1;
}
/**
* 定义切面
*/
@Pointcut("@annotation(com.sztech.common.annotation.TypeByFileValid)")
public void pt() {
}
/**
* 在方法执行后执行
* 下载/删除/上传 日志记录
*/
@AfterReturning(value = "pt()", returning = "rvt")
public void saveSuccessLog(JoinPoint joinPoint, Object rvt) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//获取注解方法
TypeByFileValid logAnnotation = method.getAnnotation(TypeByFileValid.class);
//获取注解title内容
String title = logAnnotation.title();
//获取实际方法参数
Object[] args = joinPoint.getArgs();
String name=null;
OperationLog operationLog = new OperationLog();
/**
* 由于上传/删除/下载的入参格式有区别,所以需要单独处理
*/
if (StringUtils.equals(title,"删除")){
Map<String, Object> map= (Map<String, Object>)args[0];
//该删除有可能为批量删除,需单独处理
List<String> nameList = (List<String>) map.get("xxxName");
if (ObjectUtils.isNotEmpty(nameList)&&nameList.size()>=2){
title="批量删除";
name=nameList.stream().collect(Collectors.joining(","));//若为批量删除,则存"小佳,小一,......."格式
}else {
name=nameList.get(0);
}
}else if (StringUtils.equals(title,"下载")) {
JSONObject jsonObject=(JSONObject)args[0];
name=jsonObject.getString("name");
}else if (StringUtils.equals(title,"上传文件")) {
//由于上传文件无法在入参中获取名称,所以迂回一下在出参里面获取上传文件的文件名
String jsonStr = JSON.toJSONString(rvt);
Result result = JSON.parseObject(jsonStr, Result.class);
String data = JSON.toJSONString(result.getData());
Map<String, Object> map = JSON.parseObject(data, Map.class);
name = String.valueOf(map.get("name"));
}
try {
operationLog.setName(name);
operationLog.setTitle(title);
operationLog.setStatus("成功");
OperationLogService.save(operationLog);
}catch (Exception e) {
log.info("------------------------>日志记录失败");
}
}
/**
* 单独记录失败的日志
* @param joinPoint
* @param ex
*/
@AfterThrowing(value = "pt()", throwing = "ex")
public void saveFormErrorLog(JoinPoint joinPoint, Exception ex){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//获取注解方法
TypeByFileValid logAnnotation = method.getAnnotation(TypeByFileValid.class);
//获取注解title内容
String title = logAnnotation.title();
//获取实际方法参数
Object[] args = joinPoint.getArgs();
String name=null;
OperationLog operationLog = new OperationLog();
/**
* 由于上传/删除/下载的入参格式有区别,所以需要单独处理
*/
if (StringUtils.equals(title,"删除")){
Map<String, Object> map= (Map<String, Object>)args[0];
//该删除有可能为批量删除,需单独处理
List<String> nameList = (List<String>) map.get("xxxName");
if (ObjectUtils.isNotEmpty(nameList)&&nameList.size()>=2){
title="批量删除";
name=nameList.stream().collect(Collectors.joining(","));//若为批量删除,则存"小佳,小一,......"格式
}else {
name=nameList.get(0);
}
}else if (StringUtils.equals(title,"下载")) {
JSONObject jsonObject=(JSONObject)args[0];
name=jsonObject.getString("name");
}else if (StringUtils.equals(title,"上传文件")) {
//若是上传失败,则无法记录上传失败的文件名称,这里就只记录title
//为了结构和切面成功中保持一致,这里重新获取一下title,以便更好的看清
title ="上传文件";
}
try {
String message = ex.getMessage();
if (ex instanceof BizException) {
BizException exception = (BizException) ex;
message = exception.getMsg();
}
operationLog.setLog(message);//失败则记录失败原因
operationLog.setStatus("失败");
operationLog.setTitle(title);
operationLog.setName(name);
fileOperationLogService.save(operationLog);
}catch (Exception e) {
log.info("------------------------>文件归集任务日志记录失败");
}
}
}