面向切面编程(AOP,Aspect-Oriented Programming)
面向切面编程是一种软件设计和编程范式,它旨在解决在传统面向对象编程(OOP)中难以模块化处理的“横切关注点”问题。横切关注点是指那些跨越多个对象、类或服务,并影响整个系统的行为,但与这些类的核心业务逻辑并不紧密相关的功能,例如日志记录、事务管理、安全检查、性能统计等。
AOP的主要概念:
-
切面(Aspect)
- 切面是AOP中的核心单元,它包含了应用中的一个或多个关注点的实现。切面可以包含多个通知(Advice),并在特定连接点(Join Point)上执行。
-
连接点(Join Point)
- 连接点是在程序执行过程中明确的点,如方法调用、异常抛出等,它是可以插入切面行为的地方。
-
通知(Advice)
- 通知定义了在连接点处要采取的动作类型。有多种类型的Advice:
- 前置通知(Before advice):在目标方法执行前执行。
- 后置通知(After advice):在目标方法正常返回后执行,无论方法是否抛出异常。
- 返回通知(After returning advice):仅在目标方法成功执行并返回时执行。
- 异常通知(After throwing advice):在目标方法抛出异常后执行。
- 环绕通知(Around advice):包围目标方法执行,在方法调用前后都可以进行自定义操作,并决定何时以及是否调用原方法。
- 通知定义了在连接点处要采取的动作类型。有多种类型的Advice:
-
切入点(Pointcut)
- 切入点表达式用于匹配一组连接点,定义了通知应该在哪里应用。它可以是一个方法签名、类名或者其他符合AOP框架语法的表达式,用于定位到具体的连接点集合。
-
织入(Weaving)
- 织入是将切面应用到目标对象来创建代理对象的过程。这可以在编译时(静态织入)、加载时(类装载器织入)或运行时(动态代理织入)完成。
AOP在 Spring 框架里的常见注解:
@Aspect
- 用于定义一个切面类,表明此类包含了一系列的通知方法。
@Aspect
public class LoggingAspect {
// ...
}
@Before
- 前置通知,指定一个方法作为前置增强,在目标方法执行前被调用。
-
@Before("execution(* com.example.service.MyService.*(..))") public void beforeAdvice(JoinPoint joinPoint) { // ... }
@AfterReturning
- 后置通知(正常返回后),在目标方法成功执行并返回后执行此通知方法。
@AfterReturning(pointcut = "execution(* com.example.service.MyService.get*(..))", returning = "result")
public void afterReturningAdvice(Object result) {
// ...
}
@AfterThrowing
- 异常通知,在目标方法抛出异常后执行此通知方法。
@AfterThrowing(pointcut = "execution(* com.example.service.MyService.*(..))", throwing = "ex")
public void afterThrowingAdvice(IllegalArgumentException ex) {
// ...
}
@After
- 最终通知,无论方法是否正常结束或抛出异常都会执行的方法。
@After("execution(* com.example.service.MyService.*(..))")
public void afterFinallyAdvice() {
// ...
}
@Around
- 环绕通知,最强大的通知类型,可以控制目标方法的整个执行过程,包括决定何时执行、何时结束以及修改方法的返回值等。
@Around("execution(* com.example.service.MyService.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
// 在环绕通知中,需要手动调用ProceedingJoinPoint的proceed()方法来执行原方法
// ...
return pjp.proceed();
}
@Pointcut
- 定义一个切入点表达式,可以复用这个表达式在多个通知中。
@Pointcut("execution(* com.example.service.*.*(..))")
public void businessServiceOperation() {}
@Before("businessServiceOperation()")
public void beforeBusinessService() {
// ...
}
@After("businessServiceOperation()")
public void afterBusinessService() {
// ...
}
切点表达式:
1 execution():
2.@annotation():