23. AOP实现自定义注解记录日志(复盘)
一、前言
环境说明:Windows10 + Idea2021.3.2 + Jdk1.8 + SpringBoot 2.3.1.RELEASE
前两期重点讲解了springboot 集成aop的详细用法,aop实现自定义注解,aop的几种通知类型,及aop实现自定义注解业务日志。不知道你们对于aop是否有更深一步的了解与认识。
学习了前几期aop内容,我觉得你们应该要掌握以下内容:
- 能独立实现springboot集成aop。
- 能独立实现aop自定义注解。
- 能独立实现业务日志记录并保存入库。
- ... ...
我觉得啊,如果有未掌握的知识点,一定要弄明白,毕竟这段时间,我都是在讲springboot相关的知识点,比如springboot集成redis、集成swagger、集成mail、集成mybtis-plus等,这些满满都是干货,基本都面面俱到,所以你们有啥好的主意或者建议,希望能通过私信我或者下方评论区告诉我,就算遇到我的知识盲区,我也会先去自行研究,然后再以通俗易懂的文字描述给大家听,与大家一起分享交流。好吧?
接下来,我们来全局对aop这块知识点进行复盘,差缺补漏,然后系统性的复习一遍aop素所讲述的内容。
二、内容复盘
1、自定义注解实现
还记得自定义注解是怎么实现的?若真不记得,也没关系,那么今天再看我给你讲一遍,讲完后,必须要会了啊,否则,我真会难过的。
如下,就是一个最简单的自定义注解实现方式,不知道你们能否明白呢。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UseAop {
}
首先我们要明白一件事,什么是注解?
答:注解Annotation
相当于一种标记,在程序中加入注解就等于为程序打上某种标记,随后javac编译器、开发工具和其他程序就可以通过反射来获取你的类及各种元素上有无何种标记,看你的程序有什么标记,就去执行对应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。
即注解就相当于你一个源类要调用一个类,在源程序中应用某个注解,就得在使用前必须准备好这个注解类。就像你要调用某个类,得事先开发好这个类道理是一样的。
所以,我们要对业务进行切入,那我们的切入点就是自定义注解,类或方法或属性加了自定义注解,那就能通过aop切入,在调用类或方法或属性时,可以同时干你想干的事,比如在调用方法时,我可以先执行方法前植入@before,先干点别的事,也可以在调用方法结束后@after干点自定义的事。这就是aop切面植入的好处、不仅代码耦合度低,还
提高了应用程序的可重用性,同时也提高了开发的效率
。
那我讲的这么通俗,那你们易懂了么?
2、切面类实现
自定义注解定义好了,接着就是要写植入方法了,你是想在调用方法前植入还是调用方法后植入还是调用方法报错了植入?这就是根据你的业务决定了。
通知分为5类。分别为:
- 前置通知(
Before
):在目标方法被调用之前调用通知功能。 - 后置通知(
After
):在目标方法完成之后调用通知,此时不会关心方法的输出是什么。 - 返回通知(
AfterReturning
):在目标方法成功执行之后调用通知。 - 异常通知(
AfterThrowing
):在目标方法抛出异常后调用通知。 - 环绕通知(
Around
):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
所以你就能根据实际需求进行切面植入通知了。比如我们上一期讲述的实现业务日志。就是使用的环绕通知,在你执行前获取目标方法的参数、body请求体等;在你执行后获取目标方法的返回值等。
3、业务日志记录保存入库
@Around("execution(public * com.example.demo.controller.*.*(..))")
public Object postLogAspect(ProceedingJoinPoint pjp) throws Throwable {
//初始化log
LogInfo log = this.createOpLog();
Object result = this.proceedController(pjp, log);
//获取操作类型
this.getLogType(pjp, log);
//获取返回值编码code
BaseResponse resData = this.setResponseCode(log, result);
//赋值返回编码
log.setResponseCode(resData.getCode());
//记录非成功异常
if (log.getResponseCode() != ResultEnum.SUCCESS.getKey()) {
//记录异常
log.setException(resData.getMsg());
}
//调用service保存SysLog实体类到数据库
iLogInfoService.save(log); return result;}
我的切入点是定义在类上,对需要加强访问的接口,进行访问追踪,特别进行日志增强打印及日志保存。也可以定义多个自定义注解,对你的业务进行不同的侵入性追踪。
对上述切面类中,也有一个注解特别关键,那就是@Aspect ,注解作用是把你当前类标识为一个切面供容器读取,而@Pointcut
注解,跟它也是相辅相成,作用是在注解位置上切入代码,还有一个就是device的五种通知类型,共同结合,真正实现代码切面植入。
所以在spring AOP中,业务逻辑仅仅只关注业务本身,而对一些日志记录,性能统计,安全控制,事务处理,异常处理等代码方法,会从业务逻辑代码中单独剥离出来,通过对这些行为的分离,将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。这就是使用aop的一个重要目的。代码侵入性低。如果是你面对一些代码高度黏合的业务代码,你会对这些场景进行代码分离么?你会选择对他们进行代码迭代么?