SpringAOP

1、简介

面向切面编程(AOP) 提供另外一种思考程序结构的方式来补充面向对象编程(OOP)。可以将一些方法公共的功能提取出来,单独处理。提高了代码的整洁度和健壮性。

2、AspectJ

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。(来自百度百科)
Spring 整合了AspectJ切面框架,我们大多数情况下是使用的AspectJ框架。

3、术语

  • 横切关注点: 从每个方法中抽取出来的同一类非核心业务。
  • 切面(Aspect): 封装从每个方法中抽取出来的同一类非核心业务的类。
  • 通知(Advice): 切面必须要完成的各个具体的工作。
  • 连接点(Join point): 横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置。
  • 切入点(Pointcut): 定位连接点的方式。
  • 目标(Target object): 被通知的对象。
  • 代理(AOP proxy): 向目标对象应用通知之后创建的代理对象。

图解:

4、切入点表达式和JoinPoint

4.1、切入点表达式

通过表达式的方式定位一个或多个具体的连接点。
语法格式:

1
execution([权限修饰符][返回值类型][简单类名/全类名][方法名]([参数列表]))

例子:

1
execution(* com.ld.spring.TestService.*(..))

解释:

  • 第一个“ * ”代表任意修饰符和任意返回值。
  • 第二个“ * ”代表任意方法。
  • “..”匹配任意数量、任意类型的参数。
  • 若目标类或接口与该切面类在同一包中可以省略包名。

其他使用方法:可以通过“&&”、“||”、“!”等操作符结合使用。

1
execution(* *.add*(int,..)) || execution(* *.sub*(int,..))

解释:任意类中第一个参数为int类型的以add或sub开头的方法名的方法。

4.2、JoinPoint

切入点表达式通常都会是从宏观上定位一组方法,跟具体某个通知的注解结合起来就能够确定对应的连接点。那么就一个具体的连接点而言,我们可能会关心这个连接点的一些具体信息,例如:当前连接点所在方法的方法名、当前传入的参数值等等。这些信息都封装在JoinPoint接口的实例对象中。

5、通知:

通知就是在具体的连接点上要执行的操作。一个切面可以有一个或多个通知。

  • 前置通知(Before advice):

    1. 在连接点执行之前执行。
    2. 使用@Before注解。
  • 返回通知(After returning advice):

    1. 在连接点返回结果的时候指定。
    2. 使用@AfterReturning注解。在注解中添加returning属性,就可以访问连接点的返回值。该属性的值即为用来传入返回值的参数名称。必须在通知方法的签名中添加一个同名参数。在运行SpringAOP时会通过这个参数传递返回值。
  • 异常通知(After throwing advice):

    1. 在连接点抛出异常时执行。
    2. 使用@AfterThrowing注解。在注解中添加throwing属性,就可以访问连接点抛出的异常。Throwable是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常。
    3. 如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行。
  • 后置通知(After (finally) advice):

    1. 也叫最终通知,是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候。
    2. 使用@After注解。
  • 环绕通知(Around advice):

    1. 环绕通知是所有通知类型中功能最强大的,能够全面的控制连接点,甚至可以控制是否执行连接点。
    2. 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是JoinPoint的子接口,允许控制何时执行,是否执行连接点。
    3. 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
    4. 注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用joinPoint.proceed()的返回值,否则会出现空指针异常。

6、配置方式

Spring提供了两种AOP配置方式:

  • xml模板配置

  • 注解配置

例子源码地址:
https://github.com/LDknife/spring-example/tree/master/spring-aop

7、重用切入点定义

  • 在编写AspectJ切面时,可以直接在通知注解中书写切入点表达式。但同一个切点表达式可能会在多个通知中重复出现。

  • 在AspectJ切面中,可以通过 @Pointcut 注解将一个切入点声明称简单的方法。切入点的方法体通常是空的,因为将切入点定义与应用程序逻辑混在一起是不合理的。

  • 切入点方法的访问控制符同时也控制着这个切入点的可见性。如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中。在这种情况下,它们必须被声明为public。在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。

  • 其他通知可以通过方法名称引入该切入点。

8、指定切面的优先级

  • 在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的。

  • 切面的优先级可以通过 实现Ordered接口或使用@Order注解指定

  • 实现Ordered接口,getOrder()方法返回序号。

  • 使用@Order注解,在注解中设置序号。

  • 序号值越小,优先级越高。