java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > 使用AOP切面对请求进行日志记录

SpringBoot使用AOP切面对请求进行日志记录方式

作者:小马不敲代码

这篇文章主要介绍了SpringBoot使用AOP切面对请求进行日志记录方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

简介

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过分离横切关注点(cross-cutting concerns)来提高代码的模块化。

横切关注点是指那些跨越多个模块的功能,例如日志记录、事务管理、安全性检查等。

AOP 通过将这些功能从核心业务逻辑中分离出来,使代码更易于维护和扩展。

核心概念

1、切面(Aspect):

2、连接点(Join Point):

3、通知(Advice):

4、切点(Pointcut):

5、引入(Introduction):

6、目标对象(Target Object):

目标对象是被一个或多个切面通知的对象,通常是核心业务逻辑的类。

7、代理(Proxy):

AOP 的实现方式

1、静态代理:

在编译时生成代理类,例如 AspectJ 的编译时织入。

2、动态代理:

在运行时生成代理类,例如 Spring AOP 使用的 JDK 动态代理或 CGLIB。

AOP 的应用场景

1、日志记录:

在方法调用前后记录日志,而不需要在每个方法中手动添加日志代码。

2、事务管理:

在方法执行前后管理事务的开启、提交或回滚。

3、安全性检查:

在方法调用前检查用户权限。

4、性能监控:

在方法执行前后记录时间,用于性能分析。

5、异常处理:

在方法抛出异常时执行特定的处理逻辑。

AOP 的优点

1、模块化:

将横切关注点从核心业务逻辑中分离出来,使代码更清晰、更易于维护。

2、代码复用:

通过切面实现通用功能,避免重复代码。

3、灵活性:

可以动态地添加或移除切面,而不需要修改核心业务代码。

AOP 的缺点

1、学习曲线:

对于初学者来说,AOP 的概念和实现方式可能较难理解。

2、调试困难:

由于切面逻辑是动态插入的,调试时可能难以追踪问题。

3、性能开销:

动态代理和运行时织入可能带来一定的性能开销。

AOP 的实现框架

1、Spring AOP:

Spring 框架提供的 AOP 实现,基于动态代理,易于与 Spring 集成。

2、AspectJ:

功能强大的 AOP 框架,支持编译时织入和运行时织入。

3、JBoss AOP:

JBoss 提供的 AOP 实现,支持复杂的切面逻辑。

代码实战

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<artifactId>spring-boot-demo-log-aop</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring-boot-demo-log-aop</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>com.xiaoma</groupId>
		<artifactId>spring-boot-demo</artifactId>
		<version>1.0.0-SNAPSHOT</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
		</dependency>

		<!-- 解析 UserAgent 信息 -->
		<dependency>
			<groupId>eu.bitwalker</groupId>
			<artifactId>UserAgentUtils</artifactId>
		</dependency>
	</dependencies>

	<build>
		<finalName>spring-boot-demo-log-aop</finalName>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
@Aspect
@Component
@Slf4j
public class AopLog {
	private static final String START_TIME = "request-start";

	/**
	 * 切入点
	 */
	@Pointcut("execution(public * com.xiaoma.log.aop.controller.*Controller.*(..))")
	public void log() {

	}

	/**
	 * 前置操作
	 *
	 * @param point 切入点
	 */
	@Before("log()")
	public void beforeLog(JoinPoint point) {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

		HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

		log.info("【请求 URL】:{}", request.getRequestURL());
		log.info("【请求 IP】:{}", request.getRemoteAddr());
		log.info("【请求类名】:{},【请求方法名】:{}", point.getSignature().getDeclaringTypeName(), point.getSignature().getName());

		Map<String, String[]> parameterMap = request.getParameterMap();
		log.info("【请求参数】:{},", JSONUtil.toJsonStr(parameterMap));
		Long start = System.currentTimeMillis();
		request.setAttribute(START_TIME, start);
	}

	/**
	 * 环绕操作
	 *
	 * @param point 切入点
	 * @return 原方法返回值
	 * @throws Throwable 异常信息
	 */
	@Around("log()")
	public Object aroundLog(ProceedingJoinPoint point) throws Throwable {
		Object result = point.proceed();
		log.info("【返回值】:{}", JSONUtil.toJsonStr(result));
		return result;
	}

	/**
	 * 后置操作
	 */
	@AfterReturning("log()")
	public void afterReturning() {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

		Long start = (Long) request.getAttribute(START_TIME);
		Long end = System.currentTimeMillis();
		log.info("【请求耗时】:{}毫秒", end - start);

		String header = request.getHeader("User-Agent");
		UserAgent userAgent = UserAgent.parseUserAgentString(header);
		log.info("【浏览器类型】:{},【操作系统】:{},【原始User-Agent】:{}", userAgent.getBrowser().toString(), userAgent.getOperatingSystem().toString(), header);
	}
}
@RestController
public class TestController {

	/**
	 * 测试方法
	 *
	 * @param who 测试参数
	 * @return {@link Dict}
	 */
	@GetMapping("/test")
	public Dict test(String who) {
		return Dict.create().set("who", StrUtil.isBlank(who) ? "me" : who);
	}

}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文