机缘巧合,看到一个后台管理工程,因其用Spring Boot + Shiro搭的脚手架,所以就想看看学习学习。在这中间,学到了个小技巧。运用AOP实现对用户操作日志异步存储。
AOP(面向切面编程),其弥补了OOP在横向上的局限,最为典型的就是日志管理。这也可谓是老生常谈(可以看看aop-introduction),但是我对它的认识并不深刻,今天通过这个工程剖析一下。
实现目标:用户在操作系统的每一步都记录下来,操作过程中,后台对应哪个类哪个方法等详细信息都将存入MySQL定义的日志表中,方便管理。
1.MySQL数据库中,已经有一张操作日志表,通过show create table operation_log;看看表结构情况。
2.系统中定义个POJO对象,与数据库内的表结构相对应。这其中作者选择Mybatis-Plus做为ORM框架。
3.声明一个注解,这样只要是使用该注解的地方就是AOP的切点
4.定义一个类,实现当引用该BussinessLog注解时,自动完成操作日志入库。
这里就涉及到AOP的核心内容了,让我们先来看看Spring AOP由哪些元素组成。
A pointcut declaration has two parts: a signature comprising a name and any parameters, and a pointcut expression that determines exactly which method executions we are interested in.
例如:
@Pointcut(value="@annotation(com.stylefeng.guns.common.annotion.log.BussinessLog)")//就是a pointcut expression the pointcut expression。
其匹配模式可以有如下几种:
上诉示例即对有应用com.stylefeng.guns.common.annotion.log.BussinessLog进行注解的方法进行横切面拦截。
public void cutService() {} //the pointcut signature
@Around("cutService()")
在advice中处理这个Pointcut(记录操作日志),这里的的advice使用的是Around
在Spring AOP中支持如下几种类型advice:
作者在该处运用Around advice,并通过point.proceed(),先执行切点所在位置的业务逻辑,然后通过handle(point)处理切点的业务逻辑(即将用户操作日志写入数据库)。其实,我个人觉得After returning advice更适合,这样就无须point.proceed。
ProceedingJoinPoint point该对象封装了连接点的信息,其继承自JoinPoint,该接口能够获得切点处的状态。
让我们再看看handle:
LogManager中则是通过ScheduledThreadPoolExecutor线程池,定期执行TimerTask任务。
TimerTask则是通过LogTaskFactory工厂类来创建的,其中operationLogMapper.insert(operationLog)即向数据库中插入操作日志记录,Mybatis-Plus已经为你实现,你只需要将interface OperationLogMapper extends BaseMapper<OperationLog>
总结,通过定义切点,切点处的业务处理逻辑,实现在每个操作数据库的动作记录均被切点扑捉到,并通过切点处的业务逻辑实现对动作的入库,这就是AOP的魅力。详细了解AOP请阅读aop-introduction