【SpringBoot】SpringBoot中使用AOP实现日志记录功能

avatar
作者
筋斗云
阅读量:1

427ef4152dbf4b6c92618a198935cb6c.png

  📝个人主页:哈__

期待您的关注 

1b7335aca73b41609b7f05d1d366f476.gif

目录

📕AOP简介

📂创建日志数据库 

🔥创建日志记录表

🌼创建用户表 

🍉SpringBoot使用AOP

一、导入依赖

二、创建我们的项目结构

三、使用AOP

1.创建枚举类

2..创建Log注解

3.创建切面类

4.IpUtil

5.进行测试


 

在我之前的一篇文章中我已经讲解过了AOP的基本概念,在这里无非也就是在重复一遍。

📕AOP简介

AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。


在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。


为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。

ff75df350d034af9afb288a7a9db1432.png

想要再详细了解AOP的大家可以看看我这篇文章。这篇文章我主要将在SpringBoot中使用AOP实现日志记录。

【Spring】Spring中AOP的简介和基本使用,SpringBoot使用AOP-CSDN博客

📂创建日志数据库 

🔥创建日志记录表

SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0;  -- ---------------------------- -- Table structure for sys_oper_log -- ---------------------------- DROP TABLE IF EXISTS `sys_oper_log`; CREATE TABLE `sys_oper_log`  (   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志主键',   `operation` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '操作',   `business_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '业务类型',   `method` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '方法名称',   `create_time` datetime(0) NULL DEFAULT NULL COMMENT '操作时间',   `oper_name` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作用户',   `params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '参数',   `ip` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求的ip地址',   PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2058 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '操作日志记录' ROW_FORMAT = Dynamic;  SET FOREIGN_KEY_CHECKS = 1;

🌼创建用户表 

SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0;  -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user`  (   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',   `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名 ',   `age` int(11) NULL DEFAULT NULL COMMENT '年龄 ',   `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱 ',   PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;  -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (2, 'Jack', 20, 'test2@baomidou.com'); INSERT INTO `user` VALUES (3, 'Tom', 28, 'test3@baomidou.com'); INSERT INTO `user` VALUES (4, 'Sandy', 21, 'test4@baomidou.com'); INSERT INTO `user` VALUES (5, 'Billie', 24, 'test5@baomidou.com'); INSERT INTO `user` VALUES (6, 'sss', 18, '123@qq.com'); INSERT INTO `user` VALUES (8, 'sss', 18, '123@qq.com');  SET FOREIGN_KEY_CHECKS = 1;

简单看一下表格的结构,我这里的数据就不给大家展示了。

33953114fb0840a890d0560e24ad4067.png 

f06dad005c6d405cafdaaafdf4120407.png 

🍉SpringBoot使用AOP

一、导入依赖

下边的三个依赖是我们的核心依赖。

        <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-aop</artifactId>         </dependency>          <dependency>             <groupId>mysql</groupId>             <artifactId>mysql-connector-java</artifactId>             <version>8.0.29</version>         </dependency>         <dependency>             <groupId>com.baomidou</groupId>             <artifactId>mybatis-plus-boot-starter</artifactId>             <version>3.4.3</version>         </dependency>         <dependency>             <groupId>org.projectlombok</groupId>             <artifactId>lombok</artifactId>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>

二、创建我们的项目结构

  • 创建UserMapper
@Mapper public interface UserMapper extends BaseMapper<User> { }
  • 创建UserService
public interface UserService extends IService<User> { }
  • 创建UserServiceImpl
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
  • 创建UserController
@RestController @RequestMapping("/user") public class UserController {     @Autowired     private UserService userService;     @Log(operation = "查找用户",businessType = BusinessType.LIST)     @RequestMapping("/find-user")     public String findUser(){         return userService.list().toString();     } }

创建两个实体

  • 创建User
@Data public class User {     @TableId(type = IdType.ASSIGN_ID)     private Long id;     private String name;     private Integer age;     private String email;      }
  • 创建SysOperLog
@Data @TableName("sys_oper_log") public class SysOperLog {          @TableId(type = IdType.AUTO)         private Long id;          private String operation;          private String businessType;          private String method;          @TableField(fill = FieldFill.INSERT)         private LocalDateTime createTime;          private String operName;          private String params;          private String ip; }

 

三、使用AOP

1.创建枚举类

这个枚举类的作用就是记录我们调用的接口是什么样的一个类型的,是查找、删除还是其他。

public enum BusinessType {     /**      * 其它      */     OTHER,      /**      * 新增      */     INSERT,      /**      * 修改      */     UPDATE,      /**      * 删除      */     DELETE,          /**      * 浏览      */     LIST }

2..创建Log注解

默认的操作为空,默认的操作类型是OTHER。

@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log {     /**      * 操作名称      * @return      */     String operation() default "";      /**      * 操作的类型      * @return      */     BusinessType businessType() default BusinessType.OTHER; }

3.创建切面类

@Aspect @Component public class LogAspect {     @Pointcut("@annotation(com.qcby.annotation.Log)")     public void pointCut(){}      @Autowired     HttpServletRequest request;     @Autowired     SysOperLogMapper sysOperLogMapper;      @After(value = "pointCut()")     public void afterLogWrite(JoinPoint joinPoint){         // 创建日志对象         SysOperLog sysOperLog = new SysOperLog();         // 获取我们调用的方法         MethodSignature signature = (MethodSignature) joinPoint.getSignature();         Method method = signature.getMethod();          // 获取方法上的Log注解,因为我们要获取注解中的一些信息         Log log = method.getAnnotation(Log.class);          // 获取我们调用的类的名称         String className = joinPoint.getTarget().getClass().getName();         // 获取调用的方法的名称         String methodName = method.getName();         // 重新修改一下我们调用的方法 是全路径的         methodName = className + methodName;          // 获取方法的参数         Object[] args = joinPoint.getArgs();         ObjectMapper objectMapper = new ObjectMapper();         String params = "";         try {             params =  objectMapper.writeValueAsString(args);         } catch (JsonProcessingException e) {             e.printStackTrace();         }          // 获取注解中的操作名称         String operation = log.operation();         // 获取注解中的操作类型         String businessType = log.businessType().toString();          // 这里的操作人员仅靠后端是写不了的  需要前端的token认证  我直接把操作人员改为admin         String username = "admin";          // 获取ip地址         String ipAddress = IpUtil.getIpAddr(request);          sysOperLog.setBusinessType(businessType);         sysOperLog.setOperation(operation);         sysOperLog.setMethod(methodName);         sysOperLog.setParams(params);         sysOperLog.setIp(ipAddress);         sysOperLog.setOperName(username);         sysOperLog.setCreateTime(LocalDateTime.now());                  sysOperLogMapper.insert(sysOperLog);              } } 

4.IpUtil

public class IpUtil {     private static final String UNKNOWN = "unknown";     private static final String LOCALHOST = "127.0.0.1";     private static final String SEPARATOR = ",";      public static String getIpAddr(HttpServletRequest request) {         System.out.println(request);         String ipAddress;         try {             ipAddress = request.getHeader("x-forwarded-for");             if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {                 ipAddress = request.getHeader("Proxy-Client-IP");             }             if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {                 ipAddress = request.getHeader("WL-Proxy-Client-IP");             }             if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {                 ipAddress = request.getRemoteAddr();                 if (LOCALHOST.equals(ipAddress)) {                     InetAddress inet = null;                     try {                         inet = InetAddress.getLocalHost();                     } catch (UnknownHostException e) {                         e.printStackTrace();                     }                     ipAddress = inet.getHostAddress();                 }             }             // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割             // "***.***.***.***".length()             if (ipAddress != null && ipAddress.length() > 15) {                 if (ipAddress.indexOf(SEPARATOR) > 0) {                     ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));                 }             }         } catch (Exception e) {             ipAddress = "";         }         return ipAddress;     } }

5.进行测试

我们在浏览器上输入网址:

127.0.0.1:8080/user/find-user

数据是没问题的,接下来我们只需要查看数据的日志文件是否插入了日志就好了。 

f9e9e60b8f614f0ea4582c3dafaca479.png

这里我查找了两次,一次使用的localhost,另一次使用的127.0.0.1。日志可以成功记录。 

e8a84efa2810466785895848a97810ab.png 

 

 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!