SpringBoot用AOP+自定义注解实现日志写入数据库
前言
主要以将用户操作日志写入数据库的例子简单记录AOP结合自定义注解的运用一、操作日志业务处理
根据数据库日志表和具体业务要求,写好操作日志实体类和操作日志业务层处理逻辑类OperationLogService
二、自定义注解
1.了解自定义注解
(1) 自定义注解使用@interface 用来声明,此时它自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节;(2) 在自定义注解时,不能继承其他的注解或接口;
(3) 自定义注解中的每一个方法实际上是声明的一个配置参数。其中,方法的名称就是参数的名称,返回值类型就是参数的类型(**注意** : 返回值类型只能是8种基本类型、Class、String、Enum、annotations);
(4) 自定义注解参数要求:
参数只能用public或default(默认)这两个访问权修饰,省略权限修饰词就是默认权限修饰词default;
参数类型只能用8种基本数据类型和 String,Enum,Class,annotations等数据类型,以及以上这些类型的数组;
如果只有一个参数成员,最好把参数名称设为"value",即方法名是value ;
注解参数必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解参数的值不可为 null。所以,常用空字符串或 0 作为默认值。因此每个注解的声明中,所有参数都都具有相应的值,那如何表示某个元素不存在呢,我们习惯定义一些特殊的值来表示(如空字符串或者负数);
参数的默认值可以通过 default 来声明,如 : public String name() default " "
(5) 相关元注解:
@Target 定义注解的使用位置,默认可以使用在任何元素上
ElementType.CONSTRUCTOR : 用于构造器
ElementType.FIELD : 成员变量、对象、属性(包括enum实例)
ElementType.LOCAL_VARIABLE: 用于局部变量
ElementType.METHOD : 用于方法
ElementType.PACKAGE : 用于包
ElementType.PARAMETER : 用于参数
ElementType.TYPE : 用于类、接口(包括注解类型)或enum声明
@Retention 定义注解被保留策略,生命周期
RetentionPolicy.SOURCE : 注解只保留在源文件中,在编译成class文件的时候被遗弃
RetentionPolicy.CLASS : 注解被保留在class中,但是在jvm加载运行的时候被抛弃,这个是默认的声明周期
RetentionPolicy.RUNTIME : 注解在jvm加载运行的时候仍被保留,因此它能通过反射被读取到
@Documented 表示是否将注解信息添加在Java文档中
@Inherited 表示这个自定义注解是被继承的,如果写在了父类的声明部分,那么其子类的声明部分自动拥有该注解
2.编写自定义注解
import java.lang.annotation.*;
/**
* @author nianyu
* 自定义操作日志注解(用于操作日志存入数据库操作日志表)
*/
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLogAnnotate {
/**
*操作类型
*/
String operatorType() default "";
/**
* 操作请求
*/
String operatorRequest() default "";
}
三、AOP切面
1.了解AOP切面
基本概念:(1) Aspect(切面): Aspect 声明类似于 Java 中的类声明, 切面是通知和切点的结合;
(2) Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point;
(3)Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方;
(4) Advice(通知):Advice描述了切面何时以及如何执行增强处理 ,通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码,他定义了在 Pointcut 里面定义的程序点具体要做的操作;
相关注解:
@Aspect: 定义切面类,把当前类标识为一个切面供容器读取;
@Pointcut: 切点
Pointcut是植入Advice的触发条件;Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码;
@Around:环绕增强;
@AfterReturning:后置增强,方法正常退出时执行;
@Before:前置增强,方法之前执行;
@AfterThrowing:抛出异常执行;
@After: final增强,不管是抛出异常或者正常退出都会执行;
1.编写日志切面类
/**
* 操作日志切面类
*
* @author nianyu
*/
@Aspect
@Component
public class OperationLogAspect {
/**
* OperationLogService 日志写入数据库的业务处理类
*/
@Resource
OperationLogService operationLogService;
/**
* 定义切点 @Pointcut
* 在注解的位置切入代码
* com.nianyu.log.annotation.OperationLogAnnotate 是注解所在路劲
*/
@Pointcut("@annotation(com.nianyu.log.annotation.OperationLogAnnotate)")
public void logPointCut() {
}
/**
* 用户操作完后保存日志到数据库
* 在注解的位置切入代码
* com.nianyu.log.annotation.OperationLogAnnotate 是注解所在路径
* @param ret 用户执行操作方法的返回结果,封装的类型为Result
*/
@AfterReturning(value = "logPointCut() && @annotation(logger)", returning = "ret")
public void saveOperationLog(JoinPoint joinPoint, OperationLogAnnotate logger, Result ret) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//创建一个操作日志实体类存放此次操作信息
OperationLog operationLog = new OperationLog();
try {
//获取用户id
operationLog .setOperatorId(UserUtil.getCurrentUserId().toString());
} catch (Exception e) {
e.printStackTrace();
}
//获取操作请求
operationLog.setOperatorRequest(logger.operatorRequest());
//获取操作类型
operationLog.setOperatorType(logger.operatorType());
//获取ip
operationLog.setOperatorIp(request.getRemoteAddr());
//操作时间
operationLog.setOperateDate(LocalDateTime.now());
//获取类名方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operationLog.setOperatorMethod(className + "." + methodName + "()");
//获取uri
String requestURI = request.getRequestURI();
operationLog.setOperatorUri(requestURI);
//若有返回结果,封装返回信息
if (null != ret) {
operationLog.setOperatorReturnCode(ret.getCode());
operationLog.setOperatorReturnMsg(ret.getMsg());
}
//将操作日志写入数据库
operationLogService.addOperationLog(operationLog );
}
四、操作日志写入数据库(使用自定义注解)
在需要存入数据库的操作方法上加上自定义注解
@PostMapping("/add")
@OperationLogAnnotate(operatorType="add",operatorRequest="增加XXXXX")
public Result addUser(@RequestBody User user){
return tsstService.addUser(user);
}
启动项目,访问请求,查看数据库是否有刚才的操作记录
本文介绍了如何在SpringBoot中利用AOP和自定义注解来实现操作日志的记录,并将这些日志写入数据库。首先讲解了操作日志业务处理的需求,然后详细阐述了自定义注解的创建过程,接着讨论了AOP切面的原理,并展示了如何编写日志切面类。最后,通过在业务方法上添加自定义注解,实现了当请求触发时,操作日志自动写入数据库。
292

被折叠的 条评论
为什么被折叠?



