文章目录
- AOP(面向切面编程)中的切入点(Pointcut)
- 匹配特定类中的所有方法
- 匹配特定包及其子包中所有类的所有方法
- 匹配具有特定返回类型和参数的方法
- 匹配所有public方法
- 匹配具有特定注解的方法
- AOP(面向切面编程)中的通知类型
- 前置通知@Before
- 后置通知@After
- 异常通知@AfterThrowing
- 最终通知(After (Finally) Advice)
- 环绕通知@Around
- AOP中的连接点
- ProceedingJoinPoint
- JoinPoint
AOP(面向切面编程)中的切入点(Pointcut)
在AOP(面向切面编程)中,切入点(Pointcut)是定义哪些连接点(Join Points,如方法调用、字段访问等)将被增强(即应用通知Advice)的关键部分。在Spring AOP中,切入点通常通过AspectJ的切入点表达式来定义。
切入点表达式
AspectJ切入点表达式用于匹配方法执行等连接点。这些表达式可以基于方法签名(如方法名、参数类型等)来定义。以下是一些常见的AspectJ切入点表达式示例:
匹配特定类中的所有方法
execution(* com.example.service.UserService.*(..))
这个表达式匹配com.example.service.UserService类中所有方法的执行。
匹配特定包及其子包中所有类的所有方法
execution(* com.example.service..*(..))
这个表达式匹配com.example.service包及其所有子包中所有类的所有方法的执行。
匹配具有特定返回类型和参数的方法
execution(public String com.example.service.UserService.findUser(String))
这个表达式匹配com.example.service.UserService类中名为findUser的、返回类型为String的、且接受一个String类型参数的方法的执行。
匹配所有public方法
execution(public * *(..))
这个表达式匹配所有类的所有public方法的执行。
匹配具有特定注解的方法
在Spring AOP中,虽然AspectJ切入点表达式本身不直接支持注解匹配,但你可以通过@annotation指示符在Spring的@Pointcut注解中定义这样的切入点。例如:
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
这个切入点匹配所有被@Transactional注解标记的方法。
示例
以下是一个Spring AOP的示例,展示了如何定义一个切入点并应用一个通知(在这个例子中是一个前置通知):
@Aspect
@Component
public class LoggingAspect { // 定义一个切入点,匹配UserService类中的所有方法 @Pointcut("execution(* com.example.service.UserService.*(..))") public void userServiceMethods() {} // 应用前置通知到userServiceMethods切入点 @Before("userServiceMethods()") public void logBeforeMethod(JoinPoint joinPoint) { System.out.println("Before method: " + joinPoint.getSignature().getName()); } // ... 其他通知和切入点定义
}
在这个例子中,LoggingAspect类定义了一个名为userServiceMethods的切入点,它匹配com.example.service.UserService类中所有方法的执行。然后,它定义了一个前置通知logBeforeMethod,该通知在userServiceMethods切入点匹配的方法执行之前执行,并打印出方法名。
请注意,为了使这个示例工作,你需要确保你的Spring应用程序已经启用了AspectJ自动代理,并且LoggingAspect类被Spring容器管理(例如,通过@Component注解)。此外,你还需要在Spring配置中启用AspectJ支持(如果你使用的是基于Java的配置,可以通过@EnableAspectJAutoProxy注解来实现)。
AOP(面向切面编程)中的通知类型
AOP(面向切面编程)中的通知类型主要包括以下几种,每种类型都有其特定的应用场景和示例。
前置通知@Before
定义:前置通知是在目标方法执行之前执行的通知。它不能阻止连接点之前的执行流程(除非它抛出一个异常)。
示例:
@Before("execution(* com.example.service.*.*(..))")
public void beforeAdvice() { System.out.println("前置通知:在目标方法执行之前执行");
}
在这个示例中,beforeAdvice 方法会在所有 com.example.service 包下的任意类的任意方法执行之前执行。
后置通知@After
定义:后置通知是在目标方法正常完成后执行的通知,即方法没有抛出任何异常并正常返回。
示例:
@After("execution(* com.example.service.*.*(..))")
public void afterAdvice() { System.out.println("后置通知:在目标方法执行之后执行");
}
异常通知@AfterThrowing
定义:异常通知是在目标方法抛出异常退出时执行的通知。
示例:
@AfterThrowing(value = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void afterThrowingAdvice(Exception ex) { System.out.println("异常通知:在目标方法抛出异常时执行,异常信息:" + ex.getMessage());
}
在这个示例中,afterThrowingAdvice 方法会在所有 com.example.service 包下的任意类的任意方法抛出异常时执行,并且可以通过参数 ex 捕获到抛出的异常。
最终通知(After (Finally) Advice)
定义:最终通知是在目标方法退出时执行的通知,不论是正常返回还是异常退出。
注意:在Spring AOP中,通常使用@After注解来实现最终通知的功能,因为它会在方法执行完毕后(无论是否抛出异常)执行。
环绕通知@Around
定义:环绕通知是包围一个连接点的通知,如方法调用。它是最强大的一种通知类型,可以在方法调用前后完成自定义的行为,并且可以选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
示例:
@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕通知:在目标方法执行之前执行"); try { Object result = pjp.proceed(); // 继续执行目标方法 System.out.println("环绕通知:在目标方法执行之后执行,返回结果:" + result); return result; } catch (Throwable e) { System.out.println("环绕通知:在目标方法抛出异常时执行,异常信息:" + e.getMessage()); throw e; // 可以选择重新抛出异常 }
}
在这个示例中,aroundAdvice 方法在目标方法执行前后分别执行了自定义的逻辑,并且可以在捕获到异常时进行相应的处理。
以上就是AOP中的五种主要通知类型及其示例。这些通知类型提供了强大的机制来增强或修改应用程序的行为,而无需修改源代码。
AOP中的连接点
AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,它允许开发者将横切关注点(如日志、事务管理等)与业务逻辑分离,从而提高代码的可维护性和可重用性。在Spring AOP中,连接点(JoinPoint)是一个核心概念,它指的是程序执行过程中的一个特定点,比如方法调用前、调用后或方法抛出异常时。
连接点的定义与特性
定义:
连接点指的是程序执行的某个特定位置,如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后等。在Spring AOP中,由于它仅支持方法级别的增强,因此连接点通常指的是方法的执行点。
特性:
Spring AOP中的连接点特指方法的执行。
通过连接点,可以获取到方法执行时的相关信息,如目标类名、方法名、方法参数等。
连接点是切面增强的一个潜在目标点。
获取连接点信息
在Spring AOP中,可以通过JoinPoint接口或ProceedingJoinPoint接口(它是JoinPoint的子接口)来获取连接点的信息。JoinPoint接口提供了获取目标对象、方法签名、方法参数等信息的方法,而ProceedingJoinPoint接口则在此基础上增加了proceed()方法,允许在通知中控制目标方法的执行。
ProceedingJoinPoint
对于@Around通知(环绕通知),由于它需要在目标方法执行前后都进行增强,并可能需要控制目标方法的执行,因此只能使用ProceedingJoinPoint接口。
示例代码
以下是一个使用@Around通知和ProceedingJoinPoint接口的示例代码,展示了如何在环绕通知中获取连接点信息并控制目标方法的执行:
@Aspect
@Component
public class MyAspect { @Around("execution(* com.example.service.*.*(..))") public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { // 获取目标对象的类名 String className = joinPoint.getTarget().getClass().getName(); // 获取目标方法名 String methodName = joinPoint.getSignature().getName(); // 获取目标方法运行时传入的参数 Object[] args = joinPoint.getArgs(); // 在目标方法执行前进行增强 System.out.println("Before method: " + methodName + " with arguments: " + Arrays.toString(args)); // 执行目标方法 Object result = joinPoint.proceed(); // 在目标方法执行后进行增强 System.out.println("After method: " + methodName + " returned: " + result); return result; }
}
在这个示例中,aroundAdvice方法是一个环绕通知,它使用ProceedingJoinPoint接口来获取连接点的信息,并在目标方法执行前后进行增强。通过调用joinPoint.proceed()方法,可以执行目标方法并获取其返回值。
JoinPoint
对于前置通知@Before、后置通知@AfterReturning、异常通知@AfterThrowing、最终通知@After,由于它们不需要控制目标方法的执行,因此可以使用JoinPoint接口来获取连接点的信息。
以下是一个使用@Before注解和JoinPoint参数的示例:
@Aspect
@Component
public class BeforeAdviceAspect { // 使用@Before注解定义一个前置通知 // 指定切入点表达式,匹配com.example.service包下所有类的所有方法 @Before("execution(* com.example.service.*.*(..))") public void beforeAdvice(JoinPoint joinPoint) { // 通过JoinPoint对象获取目标方法的信息 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 输出目标方法的信息 System.out.println("Before method: " + method.getName() + " in class: " + method.getDeclaringClass().getName()); // 注意:这里不能访问目标方法的参数值或返回值,因为@Before通知在方法执行之前执行 }
}
在这个示例中,beforeAdvice方法是一个前置通知,它会在com.example.service包下所有类的所有方法执行之前执行。通过JoinPoint对象,我们可以获取到关于目标方法执行点的信息,比如方法名和类名。但是,由于@Before通知在目标方法执行之前执行,因此我们不能访问目标方法的参数值或返回值。