SpringBoot系列(14): Spring AOP装逼指南之实现操作日志记录


作者: 修罗debug
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

摘要:AOP ,也称为“面向切面编程”,其大名早已如雷贯耳,是 Spring 框架的核心特性之一,相信各位小伙伴也早已听闻过,其最普遍的用法是“记录应用系统业务模块的操作日志”,今天我们就来分享介绍一下如何利用Spring AOP实现业务模块操作日志的记录。

内容:Spring  AOP,是“面向切面编程”的简称,可以起到“解耦业务模块”的作用,深层次的作用可以利用网上一位博主所说的一句话进行概括,即:

“AOP 可以实现在不修改源代码的情况下给程序动态统一添加功能,而不需要破坏某个操作业务模块代码的完整性”

对于Spring AOP,在这里有必要再啰嗦一番其核心要素,“面向切面编程”,一听就知道其核心要素是“切面”,而“切面”并非只是单纯存在的玩意,它由两大部分组成,“切点PointCut”+“通知Notice”,下面,让我用“通俗易懂”的语句表达出来吧:

“切点”:在什么地方触发我们自定义的操作(定义的是“何处”)

“通知”:在什么时候执行我们自定义的操作,同时定义好“自定义的操作”具体是什么!

“切面”:切点 + 通知;即在什么时候,在哪个地方触发执行什么样的操作!

好了,若需要具体的、深层次的关于专业术语的介绍、剖析,可以上官网或者博客找找相应的介绍吧!下面我们就进入实际的代码实战过程!

(1)首先,我们当然需要建立一个“记录日志的数据库表”,sys_log,其DDL定义如下所示:

CREATE TABLE `sys_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`operation` varchar(50) DEFAULT NULL COMMENT '用户操作',
`method` varchar(200) DEFAULT NULL COMMENT '请求方法',
`params` varchar(5000) DEFAULT NULL COMMENT '请求参数',
`time` bigint(20) NOT NULL COMMENT '执行时长(毫秒)',
`ip` varchar(64) DEFAULT NULL COMMENT 'IP地址',
`create_date` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统日志';

然后,采用Mybatis逆向工程生成相应的Entity、Mapper、Mapper.xml,之后便是等待着被调用!

(2)然后,我们定义一个注解LogAnnotation,这个注解将用于应用系统中“所有需要触发记录日志”的操作模块对应的方法之上,在后面大家会发现,正是这种方式,才实现了所谓的“服务模块解耦”、“在不修改源代码的情况下给程序动态统一添加功能”的作用。其完整源代码如下所示:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
//value将用于存储 “具体的操作模块的操作描述”
String value() default "";
}

(3)紧接着,我们需要定义一个重量级的东西,即“切面”,这个切面是基于AspectJ注解实现的类LogAspect,其完整源代码如下所示:  

/**
* 日志切面 = 切点+通知
* @Author:debug (SteadyJack)
* @Link: weixin-> debug0868 qq-> 1948831260
* @Date: 2019/11/7 15:18
**/
@Aspect
@Component
public class LogAspect {

private static final Logger log= LoggerFactory.getLogger(LogAspect.class);

@Autowired
private SysLogMapper sysLogMapper;

@Autowired
private ObjectMapper objectMapper;

//定义切点:发生的时机 - 即一旦加了这个注解,将触发某些事情
@Pointcut("@annotation(com.debug.springboot.server.annotation.LogAnnotation)")
public void logPointCut(){}

//定义通知:环绕通知 - 执行核心任务之前 + 执行完核心任务之后 该做的事情
@Around("logPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
long start=System.currentTimeMillis();
//TODO:执行核心任务
Object object=joinPoint.proceed();
long time=System.currentTimeMillis()-start;

saveLog(joinPoint,time);
return object;
}

//TODO:保存操作日志
private void saveLog(ProceedingJoinPoint point,Long time) throws Exception{
log.info("开始触发-保存操作日志");

MethodSignature signature= (MethodSignature) point.getSignature();
Method method=signature.getMethod();

SysLog entity=new SysLog();

//TODO:获取请求操作的描述信息
LogAnnotation annotation=method.getAnnotation(LogAnnotation.class);
if (annotation!=null){
entity.setOperation(annotation.value());
}

//TODO:获取操作方法名
String className=point.getTarget().getClass().getName();
String methodName=signature.getName();
entity.setMethod(className + "." + methodName + "()");

//TODO:获取请求参数
Object[] args=point.getArgs();
entity.setParams(objectMapper.writeValueAsString(args[0]));

//TODO:获取剩余参数
entity.setTime(time);
entity.setCreateDate(DateTime.now().toDate());
entity.setUsername("debug");
entity.setIp("localhost");
sysLogMapper.insertSelective(entity);
}
}

 (3)最后,我们在MultipartSourceController 中写个请求方法,并在该请求方法上加入日志注解,用于记录该请求方法在执行前、后、执行中的轨迹和相应的参数。完整的源代码如下所示:  

@LogAnnotation("多数据源时-跨事务-日志操作")
@RequestMapping(value = "update",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse update(@RequestBody LogUpdateDto dto) throws Exception{
BaseResponse response=new BaseResponse(StatusCode.Success);

User user=new User();
user.setName(dto.getName());
user.setCode(dto.getCode());
userMapper.insertSelective(user);

return response;
}

而LogUpdateDto只是一个简单的实体类:  

/**
* @Author:debug (SteadyJack)
* @Link: weixin-> debug0868 qq-> 1948831260
* @Date: 2019/11/7 16:14
**/
@Data
public class LogUpdateDto implements Serializable{

@NotBlank
private String name;

@NotBlank
private String code;
}

(4)最后,我们将整个系统跑起来,然后在Postman发起相应的请求,请求完成后,可以看到数据库表sys_log中记录了相应的请求轨迹以及相应的请求参数等等,如下图所示:


好了,本篇文章我们就介绍到这里了,其他相关的技术,感兴趣的小伙伴可以关注底部Debug的技术公众号,或者加Debug的微信,拉你进“微信版”的真正技术交流群!一起学习、共同成长!

补充

1、本文涉及到的相关的源代码可以到此地址,check出来进行查看学习:

https://gitee.com/steadyjack/SpringBootTechnology

2、目前Debug已将本文所涉及的内容整理录制成视频教程,感兴趣的小伙伴可以前往观看学习:https://www.fightjava.com/web/index/course/detail/5

3、关注一下Debug的技术微信公众号,最新的技术文章、课程以及技术专栏将会第一时间在公众号发布哦!