💥 该系列属于【SpringBoot基础】专栏,如您需查看其他SpringBoot相关文章,请您点击左边的连接
目录
3. 案例:计算PersonServiceImpl所有方法的执行时间
4. 案例:在PersonServiceImpl所有方法执行前打印方法的参数个数和类型
5. 案例:使用自定义注解@MyLog为PersonServiceImpl部分方法打印执行日志
一、AOP介绍
面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,它允许程序员通过分离横切关注点来增加模块化。在传统的面向对象编程(OOP)中,横切关注点(如日志、事务、安全等)往往散布在多个模块中,这使得代码难以维护。AOP通过在运行时将横切关注点织入到程序中,来解决这个问题。
二、AOP优点
- 提高模块化:AOP允许开发者将横切关注点从业务逻辑中分离出来,使得每个部分更加集中和模块化。
- 代码复用:横切关注点可以在多个地方重用,不需要在每个业务逻辑中重复编写。
- 维护方便:由于横切逻辑集中管理,所以维护起来更加方便。
- 减少代码耦合:通过将横切逻辑与业务逻辑分离,降低了代码间的耦合。
- 增强功能:可以在不修改源代码的情况下,动态地添加或删除功能。
三、AOP关键概念
1. 连接点
连接点(Join Point)是程序执行过程中的一个点,例如方法调用、异常抛出等。在Spring AOP中,仅支持方法的连接点。
2. 通知
通知(Advice)是指切面在特定的连接点处执行的动作。通知定义了切面是什么和何时使用。常见通知类型有:
- 前置通知(Before):在连接点之前执行。
- 后置通知(After):在连接点之后执行。
- 返回后通知(After returning):在连接点正常完成后执行。
- 抛出异常后通知(After throwing):在连接点抛出异常后执行。
- 环绕通知(Around):包围一个连接点的通知,可以在方法调用前后自定义一些操作。
3. 切入点
切入点(Pointcut)是一组连接点的集合,它定义了通知应该应用到哪些连接点上。通常使用正则表达式或特定的表达式语言来定义。
4. 切面
切面(Aspect)是通知和切入点的组合,它定义了横切逻辑,并且包含了何时(切入点)和如何(通知)应用这个逻辑。
5. 目标对象
目标对象(Target Object)是被一个或多个切面所通知的对象。在Spring AOP中,目标对象总是被代理的。
四、用法示例
1. 项目结构
2. 导入依赖
<!-- 引入Spring AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3. 案例:计算PersonServiceImpl所有方法的执行时间
(1)定义切面类 TimeAspect
@Aspect
@Component
// TimeAspect类是一个切面,它包含了通知(环绕通知)和切入点。
// 这个切面负责记录PersonServiceImpl类中方法的执行时间。
public class TimeAspect {
// 目标对象是PersonServiceImpl类的实例。
@Around("execution(* com.example.service.Impl.PersonServiceImpl.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 继续执行方法,并获取执行结果
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " 执行了 " + executionTime + " 毫秒");
return result; // 返回目标方法的执行结果
}
}
(2)接口测试
控制台输出:
Person com.example.service.Impl.PersonServiceImpl.findPersonById(int) 执行了 257 毫秒
4. 案例:在PersonServiceImpl所有方法执行前打印方法的参数个数和类型
(1)定义切面类 PreAnalysisAspect
@Aspect
@Component
public class PreAnalysisAspect {
// 定义一个切点,匹配PersonServiceImpl类中所有方法的执行
@Pointcut("execution(* com.example.service.Impl.PersonServiceImpl.*(..))")
public void serviceMethods() {
}
// 前置通知,在匹配的方法执行前执行
@Before("serviceMethods()")
public void beforeServiceMethod(JoinPoint joinPoint) {
// 获取方法签名
String methodName = joinPoint.getSignature().getName();
// 打印方法名、目标对象类型、参数类型和参数值
System.out.println("方法名: " + methodName);
// 获取目标对象类型
Class<?> targetClass = joinPoint.getTarget().getClass();
System.out.println("目标对象类型: " + targetClass.getName());
// 获取方法的参数类型和参数值
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
System.out.print("参数类型: ");
for (Class<?> parameterType : parameterTypes) {
System.out.print(parameterType.getName() + " ");
}
System.out.println();
System.out.print("参数值: ");
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.print(arg + " ");
}
System.out.println();
}
}
(2)接口测试
控制台输出:
方法名: findPersonById
目标对象类型: com.example.service.Impl.PersonServiceImpl
参数类型: int
参数值: 2
5. 案例:使用自定义注解@MyLog为PersonServiceImpl部分方法打印执行日志
(1)定义注解
package com.example.annotation;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyLog {
}
(2)定义切面类 LogAspect
@Aspect
@Component
@Slf4j
public class LogAspect {
@Pointcut("@annotation(com.example.annotation.MyLog)")
public void myLogMethods() {
}
// 环绕通知,在匹配的方法执行前后执行
@Around("myLogMethods()")
public Object aroundMyLogMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// 方法执行前的日志记录
log.info("HelloMethod before execution");
// 执行目标方法
Object result = joinPoint.proceed();
// 方法执行后的日志记录
log.info("HelloMethod after execution");
// 返回方法的执行结果
return result;
}
}
(3)service
@MyLog
public Person findPersonById(int id) {
return personMapper.findPersonById(id);
}
(4)接口测试
控制台输出:
2024-07-29 21:23:16.656 INFO 182276 --- [nio-8080-exec-1] com.example.aspect.LogAspect : HelloMethod before execution
2024-07-29 21:23:16.930 INFO 182276 --- [nio-8080-exec-1] com.example.aspect.LogAspect : HelloMethod after execution