在实际开发中我们需要对一些方法的操作进行日志的记录,比如登陆、修改密码、删除等操作记录日志,注解标记只记录需要监控的地方。
一、添加相关依赖和配置
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
在application.properties文件中添加JPA和Mysql相关配置
#mysql连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mydb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true
# JPA相关配置
spring.jpa.hibernate.ddl-auto=update
# 显示sql语句
spring.jpa.show-sql=true
二、自定义日志注解
我们创建一个自定义日志注解和操作类型的枚举类,自定义日志注解包含操作类型和日志描述信息。
示例代码如下:
import lombok.Getter;
/**
* 操作类型枚举
* 分别对应增加、删除、修改、查询、其他
*/
@Getter
public enum OperationEnum {
ADD(1, "新增"),
DELETE(2, "删除"),
UPDATE(3, "修改"),
FIND(4, "查询"),
OTHER(5, "其他");
private int code;
private String msg;
OperationEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
package com.example.myspringboot.annotation;
import com.example.myspringboot.bean.OperationEnum;
import java.lang.annotation.*;
/**
* 自定义日志注解
*/
@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface MyLog {
/**
* 日志描述信息
*/
String desc() default "";
/**
* 操作类型
*/
OperationEnum operation() default OperationEnum.OTHER;
}
三、创建日志记录实体
我们使用JPA创建日志实体类,自动在数据库中创建数据表。
示例代码如下:
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Date;
/**
* @author qx
* @date 2023/06/21
* @desc 操作日志实体
*/
@Entity
@Table(name = "t_operation_log")
@Getter
@Setter
public class OperationLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 日志描述消息
*/
private String content;
/**
* 日志操作类型
*/
private String operationType;
/**
* 日志创建时间
*/
private Date createDate;
}
接下来创建日志数据操作层。
示例代码如下:
package com.example.myspringboot.repository;
import com.example.myspringboot.bean.OperationLog;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OperationLogRepository extends JpaRepository<OperationLog, Long> {
}
四、定义切面
我们定义一个切面类,当在方法上添加了@MyLog注解时,切面就会自动织入,切面中的逻辑把日志的信息存储到数据库等存储介质。
示例代码如下:
import com.example.myspringboot.annotation.MyLog;
import com.example.myspringboot.bean.OperationLog;
import com.example.myspringboot.repository.OperationLogRepository;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Date;
/**
* @author qx
* @date 2023/06/21
* @desc 自定义日志切面
*/
@Aspect
@Component
@Slf4j
public class MyLogAspect {
@Autowired
private OperationLogRepository operationLogRepository;
/**
* 切点
*/
@Pointcut("@annotation(com.example.myspringboot.annotation.MyLog)")
private void myLogPointCut() {
}
@Around("myLogPointCut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
MyLog myLog = method.getAnnotation(MyLog.class);
// 设置日志记录
OperationLog operationLog = new OperationLog();
operationLog.setContent(myLog.desc());
operationLog.setOperationType(myLog.operation().getMsg());
operationLog.setCreateDate(new Date());
// 存储日志记录到数据库
operationLogRepository.save(operationLog);
}
}
五、定义测试类
我们在增加和删除的方法上加上我们自定义的日志注解,当我们访问这些方法的时候把注解中的信息存储到数据库。
示例代码如下:
import com.example.myspringboot.annotation.MyLog;
import com.example.myspringboot.bean.OperationEnum;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author qx
* @date 2023/06/21
* @desc 自定义日志注解测试
*/
@RestController
@RequestMapping("/user")
public class MyLogController {
/**
* 用户新增
*/
@RequestMapping("/add")
@MyLog(operation = OperationEnum.ADD, desc = "用户新增")
public String add() {
return "用户新增";
}
/**
* 用户删除
*/
@RequestMapping("/delete")
@MyLog(operation = OperationEnum.DELETE, desc = "用户删除")
public String delete() {
return "用户删除";
}
/**
* 用户查询
*/
@RequestMapping("/find")
public String find() {
return "用户查询";
}
}
六、测试
我们启动项目,JPA自动创建数据库表t_operation_log。
浏览器测试新增方法
控制台显示sql新增执行语句
2023-06-21 11:49:19.635 INFO 11760 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-06-21 11:49:19.635 INFO 11760 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-06-21 11:49:19.637 INFO 11760 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
Hibernate: insert into t_operation_log (content, create_date, operation_type) values (?, ?, ?)
我们刷新数据库,发现我们的日志的信息保存到了数据库。
测试用户删除:
我们连续刷新数据库发现删除的日志同样存储到了数据库
最后我们执行查询的方法,因为我们没有在查询的方法加上自定义日志注解,所以这个操作不会新增到数据库。
刷新数据库没有发现新增的数据。
七、总结
本文使用了自定义注解,通过在方法上标记注解实现了保存操作日志的功能。在实际工作中我们可以根据需求自定义不同的注解,比如方法的访问控制、身份认证等,提高aop自定义注解处理的多样性。