SpringBoot-AOP-Logback用切面拦截操作日志

在 Spring Boot 中使用切面来拦截操作日志,以及配合使用 MyBatis-Plus 框架进行操作,并使用 Thymeleaf 视图显示商品列表,同时配置 Logback 日志输出到文件。

sql 复制代码
CREATE TABLE product (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    description TEXT
);

INSERT INTO product (name, price, description) VALUES
    ('商品 1', 100.00, '商品描述   1'),
    ('商品 2', 150.00, '商品描述   2'),
    ('商品 3', 200.00, '商品描述   3'),
    ('商品 4', 50.00, '商品描述   4'),
    ('商品 5', 300.00, '商品描述   5'),
    ('商品 6', 120.00, '商品描述   6'),
    ('商品 7', 80.00, '商品描述   7'),
    ('商品 8', 250.00, '商品描述   8'),
    ('商品 9', 180.00, '商品描述   9'),
    ('商品 10', 90.00, '商品描述   10');
xml 复制代码
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MyBatis-Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>最新版本</version>
    </dependency>
    
      <!-- MySQL Driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>最新版本</version>
    </dependency>

    <!-- Thymeleaf -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- Jackson for JSON -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

    <!-- Logback -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
    </dependency>
</dependencies>

application.properties配置文件

bash 复制代码
# 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_database_username
spring.datasource.password=your_database_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# MyBatis-Plus 配置
mybatis-plus.mapper-locations=classpath:mapper/*.xml
mybatis-plus.global-config.id-type=auto

# Thymeleaf 配置
spring.thymeleaf.mode=HTML
spring.thymeleaf.cache=false

# 日志配置
logging.level.root=INFO
logging.level.com.icoderoad.example=DEBUG
logging.file=logs/application.log
logging.pattern.console=%msg%n

实体类Product

java 复制代码
@Data
@TableName("product")
public class Product {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private double price;
    private String description;
}

商品Mapper

java 复制代码
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}

Controller

java 复制代码
@Controller
public class ProductController{
	
	@Autowired
	private final ProductService productService;

	@GetMapping("/products")
	public String listProducts(Model model){
		model.Addtttribute("product",productService.list());
		return "product/list";
	}
}

Service

java 复制代码
public interface ProductService extends IService<Product> {
}

@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}

日志配置信息

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy-mm-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>logs/application.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>%d{yyyy-mm-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
         <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE" />
    </root>
</configuration>

WebLog类

java 复制代码
/**
 * Controller层的日志封装类
 */
@Data
@EqualsAndHashCode
public class WebLog {
    /**
     * 操作描述
     */
    private String description;

    /**
     * 操作用户
     */
    private String username;

    /**
     * 操作时间
     */
    private Long startTime;

    /**
     * 消耗时间
     */
    private Integer spendTime;

    /**
     * 根路径
     */
    private String basePath;

    /**
     * URI
     */
    private String uri;

    /**
     * URL
     */
    private String url;

    /**
     * 请求类型
     */
    private String method;

    /**
     * IP地址
     */
    private String ip;

    /**
     * 请求参数
     */
    private Object parameter;

    /**
     * 返回结果
     */
    private Object result;

}

创建切面类

拦截操作日志并将其转化为 JSON 格式,输出转换后的 JSON 数据。

java 复制代码
@Aspect
@Component
public class LogAspect{
	
	private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);

	@Autowired
	public final ObjectMapper objectMapper;

	//切入点定义:拦截所有Controller方法
	@Pointcut("execution(* com.icoderoad.example.product.controller.*.*(..))")
	public void webLog(){

	}

	//在方法返回后执行
	@AfterReturning(returning="result",pointcut="webLog()")
	public void doAfterReturning(JoinPoint joinPoint,Object result) throws Throwable  {
		
		//获取当前请求的属性
		ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();

		//创建weblog对象,并填充信息
		WebLog webLog = new WebLog();
		webLog.setStartTime(System.currentTimeMillis());
		webLog.setBasePath(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort());
		webLog.setUri(request.getRequestURI());
        webLog.setUrl(request.getRequestURL().toString());
        webLog.setMethod(request.getMethod());
        webLog.setIp(getClientIp(request));  // 获取客户端真实 IP 地址
        webLog.setParameter(Arrays.toString(joinPoint.getArgs()));
        webLog.setResult(result);

		//将WebLog对象转换为JSON格式,并输出到控制台(实际应该输出到日志文件)
		String logJson = objectMapper.writeValueAsString(webLog);
        LOGGER.info(logJson);
	}

	// 获取客户端真实 IP 地址
    private String getClientIp(HttpServletRequest request) {
        String 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 (ipAddress != null && ipAddress.contains(",")) {
            ipAddress = ipAddress.split(",")[0].trim();
        }
        return ipAddress;
    }
}

项目启动类,启动的时候扫描mapper所在的包

java 复制代码
@SpringBootApplication
@MapperScan("com.icoderoad.example.product.mapper")
public class AopLogbackProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(AopLogbackProductApplication.class, args);
    }

}

视图展示层Thymeleaf

html 复制代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
     <meta charset="UTF-8">
    <title>商品列表</title>
    <!-- 引入 Bootstrap 的 CSS 文件 -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div  class="container">
        <h1>商品列表</h1>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>名称</th>
                    <th>价格</th>
                    <th>描述</th>
                </tr>
            </thead>
            <tbody>
                <tr th:each="product : ${products}">
                    <td th:text="${product.id}"></td>
                    <td th:text="${product.name}"></td>
                    <td th:text="${product.price}"></td>
                    <td th:text="${product.description}"></td>
                </tr>
            </tbody>
        </table>
     </div>
</body>
</html>
相关推荐
WeiXiao_Hyy6 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
苏渡苇12 分钟前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
long31623 分钟前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
独断万古他化33 分钟前
【SSM开发实战:博客系统】(三)核心业务功能开发与安全加密实现
spring boot·spring·mybatis·博客系统·加密
rannn_1111 小时前
【苍穹外卖|Day4】套餐页面开发(新增套餐、分页查询、删除套餐、修改套餐、起售停售)
java·spring boot·后端·学习
qq_12498707531 小时前
基于JavaWeb的大学生房屋租赁系统(源码+论文+部署+安装)
java·数据库·人工智能·spring boot·计算机视觉·毕业设计·计算机毕业设计
短剑重铸之日1 小时前
《设计模式》第十一篇:总结
java·后端·设计模式·总结
倒流时光三十年1 小时前
SpringBoot 数据库同步 Elasticsearch 性能优化
数据库·spring boot·elasticsearch
码农小卡拉2 小时前
深入解析Spring Boot文件加载顺序与加载方式
java·数据库·spring boot
Dragon Wu2 小时前
Spring Security Oauth2.1 授权码模式实现前后端分离的方案
java·spring boot·后端·spring cloud·springboot·springcloud