AOP(Aspect-Oriented Programming)是一种编程范式,它允许开发人员将横切关注点(如日志记录、性能统计、安全性等)从主要业务逻辑中分离出来,以便更好地模块化和管理这些关注点。
AOP通过定义切面(Aspect)来实现这一目标,切面包含了通知(Advice)和切点(Pointcut),通知定义了在何时、何地执行横切逻辑,而切点定义了在何处执行横切逻辑。通过将切面与应用程序的其他部分进行连接,AOP可以实现横切关注点的统一管理和重用。
AOP通常用于解决跨越多个模块的横切关注点,例如日志记录、事务管理、安全性等。
我们可以简单的把 AOP 理解为贯穿于方法之中,在方法执行前、执行时、执行后、返回值后、异常后要执行的操作。
切面(Aspect): 对横切关注点进行封装的类,每个关注点体现为一个通知方法;通常使用 @Aspect 注解来定义切面。
通知(Advice): 切面必须要完成的各个具体工作,比如我们的日志切面需要记录接口调用前后的时长,就需要在调用接口前后记录时间,再取差值。通知的方式有五种:
连接点(JoinPoint): 通知应用的时机,比如接口方法被调用时就是日志切面的连接点。
切点(Pointcut): 通知功能被应用的范围,比如本篇日志切面的应用范围是所有 controller 的接口。通常使用 @Pointcut 注解来定义切点表达式。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLog {
}
@Slf4j
@Aspect
@Component
public class ApiLogAspect {
@Pointcut("@annotation(com.itwenke.springbootdemo.aop.aspect.ApiLog) || @within(com.itwenke.springbootdemo.aop.aspect.ApiLog)")
public void getLogAnnotation() {
}
@Around("getLogAnnotation()")
public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
Object ans = joinPoint.proceed();
return ans;
} finally {
log.info("执行耗时: {}#{} = {}ms",
joinPoint.getSignature().getDeclaringType().getSimpleName(),
joinPoint.getSignature().getName(),
System.currentTimeMillis() - start);
}
}
}
@RequestMapping(path = "api/test")
@RestController
public class TestController {
@RequestMapping(path = "hi")
@ApiLog()
public String hi() {
return "hi";
}
@RequestMapping(path = "select")
@ApiLog()
public String select() throws InterruptedException {
Thread.sleep(5000);
return "select";
}
}
http://localhost:8080/api/test/hi
http://localhost:8080/api/test/select
2024-01-02 15:32:33.567 INFO 4548 --- [nio-8080-exec-2] c.i.s.aop.aspect.ApiLogAspect : 执行耗时: TestController#hi = 0ms
2024-01-02 15:32:42.417 INFO 4548 --- [nio-8080-exec-5] c.i.s.aop.aspect.ApiLogAspect : 执行耗时: TestController#select = 5005ms