1. @Around
注解
@Around
是一种环绕通知(Around Advice),它允许你在目标方法执行前后都执行一些逻辑。这意味着你可以在方法调用之前、之后甚至在方法抛出异常时执行特定的逻辑。
示例
@Around("@annotation(myLock)")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
// 前置逻辑
System.out.println("Before method execution");
// 执行目标方法
Object result = joinPoint.proceed();
// 后置逻辑
System.out.println("After method execution");
return result;
}
特点
- 灵活性高:可以在方法调用前后执行任意逻辑。
- 控制方法执行:可以决定是否继续执行目标方法(通过
proceed()
方法)。 - 捕获异常:可以在方法抛出异常时捕获并处理。
2. @Pointcut
注解
@Pointcut
用于定义切点(Pointcut),即确定哪些方法需要被拦截。切点表达式可以非常灵活,可以基于方法名、类名、注解等多种条件来定义。
示例
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {
}
@Before("serviceMethods()")
public void beforeServiceMethods(JoinPoint joinPoint) {
System.out.println("Before service method execution");
}
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void afterServiceMethods(JoinPoint joinPoint, Object result) {
System.out.println("After service method execution, result: " + result);
}
特点
- 定义切点:用于定义一组匹配的方法,这些方法将成为通知的目标。
- 复用性高:可以被多个通知(如
@Before
、@AfterReturning
、@AfterThrowing
、@Around
)共享。 - 简化配置:避免在多个通知中重复相同的切点表达式。
区别总结
-
作用不同:
@Around
:定义环绕通知,可以在目标方法执行前后执行逻辑。@Pointcut
:定义切点,确定哪些方法需要被拦截。
-
使用场景不同:
@Around
:适用于需要在方法调用前后执行复杂逻辑的场景,例如事务管理、日志记录等。@Pointcut
:适用于需要定义一组方法作为通知目标的场景,提高代码的复用性和可维护性。
-
语法和参数不同:
@Around
:需要一个ProceedingJoinPoint
参数,通过proceed()
方法执行目标方法。@Pointcut
:通常没有参数,只是一个无参方法,用于定义切点表达式。
结合使用
你可以在同一个切面类中同时使用 @Pointcut
和 @Around
,以提高代码的可读性和复用性。
@Aspect
@Component
public class MyAspect {
@Pointcut("@annotation(myLock)")
public void myLockPointcut() {
}
@Around("myLockPointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
// 前置逻辑
System.out.println("Before method execution with myLock annotation");
// 执行目标方法
Object result = joinPoint.proceed();
// 后置逻辑
System.out.println("After method execution with myLock annotation");
return result;
}
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {
}
@Before("serviceMethods()")
public void beforeServiceMethods(JoinPoint joinPoint) {
System.out.println("Before service method execution");
}
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void afterServiceMethods(JoinPoint joinPoint, Object result) {
System.out.println("After service method execution, result: " + result);
}
}
通过这种方式,你可以更灵活地管理和复用切点表达式,使代码更加清晰和模块化。
同时,一个切面可以对多个表达式进行监听。通过在同一个切面类中定义多个 @Pointcut
方法,并在不同的通知方法中引用这些切点表达式,你可以实现对多个表达式的监听。这种方式不仅提高了代码的复用性,还使得切点表达式的管理更加清晰和集中。
示例
假设我们有一个切面类 MyAspect
,需要对带有 @MyLock
注解的方法和 com.example.service
包下的所有方法进行监听。
1. 定义切点表达式
首先,在切面类中定义多个 @Pointcut
方法,每个方法对应一个切点表达式。
@Aspect
@Component
public class MyAspect {
@Pointcut("@annotation(myLock)")
public void myLockPointcut() {
}
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethodsPointcut() {
}
}
2. 定义通知方法
接下来,在同一个切面类中定义多个通知方法,并在这些通知方法中引用相应的切点表达式。
环绕通知 (@Around
)
@Around("myLockPointcut()")
public Object aroundAdviceWithMyLock(ProceedingJoinPoint joinPoint) throws Throwable {
// 前置逻辑
System.out.println("Before method execution with myLock annotation");
// 执行目标方法
Object result = joinPoint.proceed();
// 后置逻辑
System.out.println("After method execution with myLock annotation");
return result;
}
@Around("serviceMethodsPointcut()")
public Object aroundAdviceForServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
// 前置逻辑
System.out.println("Before service method execution");
// 执行目标方法
Object result = joinPoint.proceed();
// 后置逻辑
System.out.println("After service method execution");
return result;
}
前置通知 (@Before
)
@Before("myLockPointcut()")
public void beforeAdviceWithMyLock(JoinPoint joinPoint) {
System.out.println("Before method execution with myLock annotation");
}
@Before("serviceMethodsPointcut()")
public void beforeAdviceForServiceMethods(JoinPoint joinPoint) {
System.out.println("Before service method execution");
}
后置返回通知 (@AfterReturning
)
@AfterReturning(pointcut = "myLockPointcut()", returning = "result")
public void afterReturningAdviceWithMyLock(JoinPoint joinPoint, Object result) {
System.out.println("After method execution with myLock annotation, result: " + result);
}
@AfterReturning(pointcut = "serviceMethodsPointcut()", returning = "result")
public void afterReturningAdviceForServiceMethods(JoinPoint joinPoint, Object result) {
System.out.println("After service method execution, result: " + result);
}
异常通知 (@AfterThrowing
)
@AfterThrowing(pointcut = "myLockPointcut()", throwing = "ex")
public void afterThrowingAdviceWithMyLock(JoinPoint joinPoint, Throwable ex) {
System.out.println("Exception in method execution with myLock annotation: " + ex.getMessage());
}
@AfterThrowing(pointcut = "serviceMethodsPointcut()", throwing = "ex")
public void afterThrowingAdviceForServiceMethods(JoinPoint joinPoint, Throwable ex) {
System.out.println("Exception in service method execution: " + ex.getMessage());
}
总结
通过在同一个切面类中定义多个 @Pointcut
方法,并在不同的通知方法中引用这些切点表达式,你可以实现对多个表达式的监听。这种方式不仅提高了代码的复用性,还使得切点表达式的管理更加清晰和集中。以下是一些关键点:
- 定义切点表达式:使用
@Pointcut
注解定义切点表达式,每个切点表达式对应一个方法。 - 引用切点表达式:在通知方法中使用切点表达式的名字来引用对应的切点。
- 多种通知类型:可以在同一个切面类中定义多种通知类型(如
@Around
、@Before
、@AfterReturning
、@AfterThrowing
),并分别引用不同的切点表达式。