Spring学习笔记——(5)AOP(二)

一、前置通知

(1)在方法执行之前执行的通知。前置通知使用 @Before 注解, 并将切入点表达式的值作为注解值。

/**
    * 前置通知,在目标方法开始之前执行。
* 1.@Before 标记的方法的方法体.
* 2.@Before 里面的是切入点表达式
* 3.JoinPoint 类型的参数:从中可以访问到方法的签名和方法的参数. */ @Before("execution(public int com.shaohe.spring.aop.helloword.ArithmeticCalculator.*(int, int))") public void beforeMethod(JoinPoint joinPoint) { // 方法名称 String methodName = joinPoint.getSignature().getName(); // 参数数组 Object[] args = joinPoint.getArgs(); System.out.println("The method " + methodName + " begins with " + Arrays.asList(args)); }

(2)利用方法签名编写 AspectJ 切入点表达式

  • execution * com.atguigu.spring.ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 中声明的所有方法,第一个 * 代表任意修饰符及任意返回值. 第二个 * 代表任意方法. .. 匹配任意数量的参数. 若目标类与接口与该切面在同一个包中, 可以省略包名.
  • execution public * ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 接口的所有公有方法.
  • execution public double ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 中返回 double 类型数值的方法
  • execution public double ArithmeticCalculator.*(double, ..): 匹配第一个参数为 double 类型的方法, .. 匹配任意数量任意类型的参数
  • execution public double ArithmeticCalculator.*(double, double): 匹配参数类型为 double, double 类型的方法.

(3)在 AspectJ 中, 切入点表达式可以通过操作符 &&, ||, ! 结合起来. 

Spring学习笔记——(5)AOP(二)_第1张图片

二、后置通知

在目标方法执行后(无论是否发生异常),执行的通知。

后置通知是在连接点完成之后执行的, 即连接点返回结果或者抛出异常的时候

/**
 * 后置通知:@After,在方法执行之后执行的代码.
 */
@After("execution(* com.shaohe.spring.aop.helloword.*.*(..))")
public void afterMethod(JoinPoint joinPoint) {
	String methodName = joinPoint.getSignature().getName();
	System.out.println("The method " + methodName + " ends");
}

三、返回通知

在方法正常结束后执行

/**
 * 返回通知:在方法正常结束后执行的代码
* 返回通知是可以访问到方法的返回值的!
* value属性或者pointcut属性中填写切点表达式
* returning:和通知的参数名相同,都是result */ @AfterReturning(value = "execution(* com.shaohe.spring.aop.helloword.*.*(..))", returning = "result") public void afterReturning(JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().getName(); System.out.println("返回通知:The method " + methodName + " ends with " + result); }
  • 在返回通知中, 只要将 returning 属性添加到 @AfterReturning 注解中, 就可以访问连接点的返回值. 该属性的值即为用来传入返回值的参数名称. 
  • 必须在通知方法的签名中添加一个同名参数. 在运行时, Spring AOP 会通过这个参数传递返回值.

四、异常通知

只在连接点抛出异常时才执行

/**
 * 异常通知:只在连接点抛出异常时才执行
 */
@AfterThrowing(value = "execution(* com.shaohe.spring.aop.helloword.*.*(..))", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
	String methodName = joinPoint.getSignature().getName();
	System.out.println("异常通知:The method " + methodName + " occurs excetion:" + e);
}
  • throwing 属性添加到 @AfterThrowing 注解中, 也可以访问连接点抛出的异常. Throwable 是所有错误和异常类的超类. 所以在异常通知方法可以捕获到任何错误和异常.
  • 如果只对某种特殊的异常类型感兴趣, 可以将参数声明为其他异常的参数类型. 然后通知就只在抛出这个类型及其子类的异常时才被执行.

五、环绕通知

  • 环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是否执行连接点.
  • 对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点.
  • 在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.
  • 注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常
/**
 * 环绕通知:相当于动态代理的全过程
* ProceedingJoinPoint类型的参数可以决定是否执行目标方法
* 必须有返回值,即目标方法的返回值。 */ @Around("execution(* com.shaohe.spring.aop.helloword.*.*(..))") public Object aroundMethod(ProceedingJoinPoint pjd) { Object result = null; String methodName = pjd.getSignature().getName(); try { // 前置通知 System.out.println("环绕通知-前置通知:The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs())); // 执行目标方法 result = pjd.proceed(); // 返回通知 System.out.println("环绕通知-返回通知:The method " + methodName + " ends with " + result); } catch (Throwable e) { // 异常通知 System.out.println("环绕通知-异常通知:The method " + methodName + " occurs exception:" + e); throw new RuntimeException(e); } // 后置通知 System.out.println("环绕通知-后置通知:The method " + methodName + " ends"); // 返回值,即目标方法的返回值。 return result; }

六、指定切面的优先级

  • 在同一个连接点上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的.
  • 切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定.
  • 实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.
  • 若使用 @Order 注解, 序号出现在注解中

Spring学习笔记——(5)AOP(二)_第2张图片

七、重用切入点定义

  • 在编写 AspectJ 切面时, 可以直接在通知注解中书写切入点表达式. 但同一个切点表达式可能会在多个通知中重复出现.
  • 在 AspectJ 切面中, 可以通过 @Pointcut 注解将一个切入点声明成简单的方法. 切入点的方法体通常是空的, 因为将切入点定义与应用程序逻辑混在一起是不合理的. 
  • 切入点方法的访问控制符同时也控制着这个切入点的可见性. 如果切入点要在多个切面中共用, 最好将它们集中在一个公共的类中. 在这种情况下, 它们必须被声明为 public. 在引入这个切入点时, 必须将类名也包括在内. 如果类没有与这个切面放在同一个包中, 还必须包含包名.
  • 其他通知可以通过方法名称引入该切入点.

Spring学习笔记——(5)AOP(二)_第3张图片

八、基于 XML 的配置声明切面

  • 当使用 XML 声明切面时, 需要在 根元素中导入 aop Schema
  • 在 Bean 配置文件中, 所有的 Spring AOP 配置都必须定义在 元素内部. 对于每个切面而言, 都要创建一个 元素来为具体的切面实现引用后端 Bean 实例. 
  • 切面 Bean 必须有一个标示符, 供 元素引用

声明切面:


public class LoggingAspectXML {

	/**
	 * 前置通知,在目标方法开始之前执行
	 */
	public void beforeMethod(JoinPoint joinPoint) {
		// 方法名称
		String methodName = joinPoint.getSignature().getName();
		// 参数数组
		Object[] args = joinPoint.getArgs();
		System.out.println("前置通知:The method " + methodName + " begins with " + Arrays.asList(args));
	}

	/**
	 * 后置通知:,在方法执行之后执行的代码.
	 */
	public void afterMethod(JoinPoint joinPoint) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("后置通知:The method " + methodName + " ends");
	}

	/**
	 * 返回通知:在方法正常结束后执行的代码
* 返回通知是可以访问到方法的返回值的!
*/ public void afterReturning(JoinPoint joinPoint, Object result) { String methodName = joinPoint.getSignature().getName(); System.out.println("返回通知:The method " + methodName + " ends with " + result); } /** * 异常通知:只在连接点抛出异常时才执行 */ public void afterThrowing(JoinPoint joinPoint, Exception e) { String methodName = joinPoint.getSignature().getName(); System.out.println("异常通知:The method " + methodName + " occurs excetion:" + e); } }

 

xml配置:









	
	
	
	
		
		
		
		
		
		
		
		
		
		
	

启动测试:

public static void main(String[] args) {

	ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-aop-xml.xml");
	// 从IOC容器中获取Bean的实例
	ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
	// bean的类型名称
	System.out.println(arithmeticCalculator.getClass().getName());
	// 调用bean的方法。
	int result = arithmeticCalculator.add(11, 12);
	System.out.println("result:" + result);

}

九、引入通知(不常用)

引入通知是一种特殊的通知类型. 它通过为接口提供实现类, 允许对象动态地实现接口, 就像对象已经在运行时扩展了实现类一样.

Spring学习笔记——(5)AOP(二)_第4张图片

  • 引入通知可以使用两个实现类 MaxCalculatorImpl 和 MinCalculatorImpl, 让 ArithmeticCalculatorImpl 动态地实现 MaxCalculator 和 MinCalculator 接口. 而这与从 MaxCalculatorImpl 和 MinCalculatorImpl 中实现多继承的效果相同. 但却不需要修改 ArithmeticCalculatorImpl 的源代码
  • 引入通知也必须在切面中声明
  • 在切面中, 通过为任意字段添加@DeclareParents 注解来引入声明. 
  • 注解类型的 value 属性表示哪些类是当前引入通知的目标. value 属性值也可以是一个 AspectJ 类型的表达式, 以将一个即可引入到多个类中.  defaultImpl 属性中指定这个接口使用的实现类

Spring学习笔记——(5)AOP(二)_第5张图片

你可能感兴趣的:(Spring系列)