Spring AOP

简介

AOP(面向切面编程,Aspect-Oriented Programming)是一种新的方法论,是对OOP(面向对象编程,Object-Oriented Programming)的补充。
AOP的主要编程对象是aspect(切面),而切面模块化了横切关注点


AOP术语

  • 切面(Aspect):
    与类相似,类是对物体特征的抽象;切面是对横切关注点的抽象。切面包含切入点的描述和通知的描述。
  • 连接点(JointPoint):
    被拦截的点。在Spring中只支持方法型的连接点,实际上,连接点也可以是field或constructor。
  • 切入点(PointCut):
    定义拦截哪些连接点。
  • 通知(Advice):
    拦截到joinpoint之后要做的事情。包括,前置通知、后置通知、异常通知、最终通知以及环绕通知。
  • 目标(Target):
    代理的目标对象
  • 织入(Weave):
    将aspect应用到target并导致proxy对象创建的过程。
  • 引入(Introduction):
    在不改变类代码的前提下,Introduction可以在运行期动态地为类添加一些方法或field。

使用切面

在Spring中有两种方式使用切面:

  • 基于AspectJ注解
  • 使用XML配置文件

基于AspectJ注解的方式

1,使用AspectJ注解
  • 要在Spring应用中使用AspectJ注解,必须在CLASSPATH下包含 AspectJ类库:aopalliance.jar、aspectjweaver.jar、spring-aspects.jar
  • 将aop schema添加到<beans>根元素中:
    1111
  • 要在Spring IOC容器中启用AspectJ注解支持,只需要在Bean配置文件中,定义一个空的<aop:aspectj-autoproxy>元素:
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    当Spring IOC容器侦测到Bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为 与AspectJ切面 相匹配的Bean创建代理
2,关于切面
  • 切面是IOC容器中的bean
  • 切面还要加入@Aspect注解
3,关于通知
  • 通知其实是一个方法
  • 需要在通知方法前面加上@Before@After@AfterReturning@AfterThrowing@Around注解
  • 可以在通知方法中,声明一个类型为JoinPoint的参数,可以通过它来访问连接点的细节,比如方法名称、参数值等。
  • AspectJ有五种类型的通知注解:
    • @Before - 前置通知,在方法执行之前执行
    • @After - 后置通知,在方法执行之后执行
      • 无论方法正常返回还是抛出异常,后置通知都会执行
    • @AfterReturning - 返回通知,在方法返回结果之后执行
      • 只有方法正常返回的时候,才会执行返回通知
      • 在返回通知中,可以访问连接点的返回值:
        1. returning属性添加到@AfterReturning注解中
        2. 必须在通知方法的签名中,添加一个同名参数。在运行时,Spring AOP会通过这个参数传递返回值
        3. 原始的切入点表达式,需要出现在pointcut属性中
    • @AfterThrowing - 异常通知,在方法抛出异常之后执行
      • 只有连接点抛出异常时,才执行异常通知
      • 为了访问连接点抛出的异常,可以将throwing属性添加到@AfterThrowing注解中
      • 如果只对某种特殊的异常类型感兴趣,可以将参数声明为该异常类型。然后通知就只在连接点抛出该类型或其子类型的异常时被执行
    • @Around - 环绕通知,围绕着方法执行
      • 可以控制是否执行连接点
      • 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint,它是JoinPoint的子接口,允许控制何时执行以及是否执行连接点
      • 在环绕通知中需要明确的调用ProceedingJoinPointproceed()方法,来执行被代理的方法。如果忘记这样做就会导致环绕通知被执行了,但是目标方法没有被执行
      • 环绕通知需要返回目标方法执行之后的返回结果,即调用ProceedingJoinPoint的返回值。否则会出现空指针异常

例1-使用AspectJ注解以及通过JoinPoint参数访问连接点信息:
2222 例2-在返回通知中访问连接点的返回值: 4444 例3-在异常通知中,只关注特定的异常(本例中是ArithmeticException): 5555 例4-环绕通知示例:
6666

4,关于AspectJ切入点表达式
  • 最典型的切入点表达式是根据方法的签名来匹配各种方法
    • execution(* cn.timd.spring.aop.IBusiness.* (..))
      • 第一个“*”表示任意修饰符以及任意返回类型
      • 第二个“*”表示任意方法
      • “..”表示匹配任意数量的参数
      • 如果目标类和接口与该切面在同一个包中,可以省略包名
    • execution(public * IBusiness.* (..))
      • 表示IBusiness接口中的所有公有方法
    • execution(public double IBusiness.* (..))
      • 表示IBusiness接口中的所有返回类型为double的公有方法
    • execution(public double IBusiness.* (double, ..))
      • 匹配IBusiness接口中,所有返回类型为double,并且第一个参数的类型也是double的公有方法
    • execution(public double IBusiness.* (double, double))
      • 匹配IBusiness接口中,满足下面2个条件的公有方法:a)返回类型是double;b)拥有两个参数,并且都为double型
  • 合并切入点表达式
    • 切入点表达式可以通过&&、||、!结合起来
      3333
5,关于切面的优先级
  • 当在同一个连接点上有多个切面的时候,如果没有明确指定,那么切面的优先级是不确定的
  • 切面的优先级可以通过实现Ordered接口或者使用@Order注解来指定
  • 在实现Ordered接口的时候,getOrder()方法的返回值越小,优先级越高
  • 使用@Order注解的时候,序号出现在注解中
6,重用切入点定义
  • 在AspectJ切面中,可以通过@Pointcut注解将一个切入点声明成简单的方法。切入点的方法体通常是空的,因为将切入点定义和应用程序逻辑混在一起是不合理的
  • 切入点的访问控制符,同时也控制着切入点的可见性
  • 在通知中可以通过方法名称来引用该切入点

例1-通过@Pointcut注解将切入点声明成方法:
7777


基于XML的配置声明切面

1,概述
  • 除了使用AspectJ注解的方式声明切面,Spring也支持在bean配置文件中声明切面。这种声明是通过aop schema中的XML元素完成的
  • 正常情况下,基于AspectJ注解的声明要优先于基于XML的声明,因为通过AspectJ注解,切面可以与AspectJ兼容;而基于XML的配置则是Spring专有的
2,基于XML---声明切面
  • <beans>根元素中导入aop Schema
  • 在Bean配置文件中,所有的Spring AOP配置都必须定义在<aop:config>元素内部。对于每个切面而言,都要创建一个<aop:aspect>元素,来为具体的切面引用后端的Bean实例
  • 切面Bean必须有一个标识符,以供<aop:aspect>元素引用
3,基于XML---声明切入点
  • 切入点使用<aop:pointcut>元素声明
  • 切入点必须定义在<aop:config><aop:aspect>元素内部:
    • 当定义在<aop:config>元素内部时,对所有切面有效
    • 当定义在<aop:aspect>元素内部时,只对当前切面有效
4,基于XML---声明通知
  • 使用<aop:before>声明前置通知
  • 使用<aop:after>声明后置通知
  • 使用<aop:after-returning>声明返回通知
  • 使用<aop:after-throwing>声明异常通知
  • 使用<aop:around>声明环绕通知
  • 使用pointcut-ref属性引用切入点
  • 使用method属性指定通知方法

例子: 8888

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: