面向切面的Spring

来源:互联网 发布:淘宝客服辛苦吗 编辑:程序博客网 时间:2024/04/30 12:15

内容概要:

  • 面向切面的基本原理
  • 通过POJO创建切面
  • 使用@AspectJ注解
  • 为@AspectJ切面注入依赖

在软件开发中 , 散步在应用中多处的功能被称为横切关注点 , 通常来讲 , 这些横切关注点从概念上是与应用的业务逻辑相分离的 , 但是往往会嵌入到应用的业务逻辑中 , 所以 , 把这些横切关注点与业务逻辑相分离就是面向切面编程要做解决的问题 —-AOP

DI有助于应用对象之间的解耦 , 而AOP可以实现横切关注点与他们所影响的对象之间的解耦 .

什么是面向切面编程

这里写图片描述

上图展现了一个被划分模块的典型应用 , 每个模块的核心功能都是为特定业务领域提供服务 , 但是这些模块都需要辅助类似的功能 , 例如安全和事物管理 , 如果要重用这些通用功能的话 , 最常见的面向对象技术是继承或委托 , 但是 ,如果整个应用中都使用相同的基类 , 继承往往会导致一个脆弱的对象体系 .

切面提供了取代继承和委托的另一种可选方案 , 在使用面向切面编程的时候 , 任然需要在一个地方定义通过功能 , 但是可以通过声明的方式定义这个功能要以何种方式在何处应用 , 而不需要修改受影响的类 .

横切关注点可以可以被模块化为特殊的类 , 这些类被称为切面(aspect) ,

这样做的好处 : 每个关注点都集中在一个地方 , 而不是分散到多处代码中 , 其次 , 服务模块更加简洁 , 因为他们只包含主要关注点(核心功能)的代码 , 而次要关注点都被转移到切面中了 .

定义AOP术语

常用术语:

  • 通知 advice
  • 切点pointcut
  • 连接点join point

通知advice


在aop术语中 , 切面的工作被称为通知 , 通知定义了切面是什么以及何时调用 , 除了描述切面要完成的工作 , 通知还解决了何时执行这个工作的问题 , 它(这个功能)应该应用在某个方法调用之前?还是之后? 还是之前之后都调用 , 还是只在方法抛出异常后使用 …


Spring切面可以应用的五种类型的通知:

  • 前置通知 : Before 在目标方法调用之前调用通知功能
  • 后置通知 : After 在目标方法调用之后调用通知功能
  • 返回通知 : After-returning 在目标方法成功执行后调用通知功能
  • 异常通知 : After-throwing 在目标方法抛出异常后调用通知
  • 环绕通知 : Around 通知包裹了目标方法 , 在被通知的方法调用之前和调用之后执行自定义的行为

连接点 Join point


应用有很多时机应用通知 , 这些时机被称为连接点 , 连接点是应用执行流程过程中能过插入切面的一个点 , 这个点可以是调用方法 , 抛出异常 , 甚至修改一个字段 的时候 , 切面代码可以利用这些点插入到应用的正常流程之中 , 并添加新的行为


切点 Pointcat


一个切面并不需要通知应用的所有连接点 , 切点就有助于缩小切面所通知的连接点的范围 .
如果说通知定义了切面的”什么”跟”什么时候”的话 , 那么切点就定义了”什么地方” , 切点的定义会匹配通知所要织入的一个或者多个连接点 . 通常使用明确的类和方法名称 , 或是利用正则表达式所匹配的类和方法名来指定这些切点 , 有些AOP框架允许创建动态的切点 , 可以根据运行的决策(比如方法的参数值)来决定是否应用通知 .


切面Aspect


切面是通知和切点的结合 , 通知和切点共同定义了切面的全部内容—他是什么 , 他在什么时候干什么 .


引入(Indroduction)


引入允许向类添加新方法或者新属性 , 可以在无需修改现有类的情况下 , 让他们具有新的行为和动态 .


织入(Weaving)


织入是把切面应用到目标对象 , 并创建新的代理对象的过程 , 切面在指定的连接点被织入到目标对象 , 而且在目标对象的生命周期里有多个点可以进行织入 :

  • 编译期:切面在目标类编译时织入 , 这种方式需要特殊的编译器 , AspectJ的织入编译器就是以这种方式织入切面的 .
  • 类加载期 : 切面在目标类加载到JVM时被织入 , 这种方式需要特使的类加载器 , 他可以在目标类被引入应用之前增强该目标类的字节码 , AspectJ 5 的加载时织入就支持以这种方式织入切面
  • 运行期 : 切面在应用运行的某个时刻被织入被织入 , 一般情况下 , 在织入切面时 , AOP容器会为目标对象动态的创建一个代理对象 , Spring AOP就是以这种方式织入切面的

小结


通知包含了需要用于多个对象的横切行为 ;
连接点是程序执行过程中能够应用通知的所有点 ;
切点定义了通知被应用的的具体位置 (在那些连接点)
其中关键的概念是切点定义了哪些连接点会得到通知.


Spring对AOP的支持


并不是所有的AOP框架都是相同的 , 但是无论如何 , 创建切点来定义切面切面所织入的连接点是AOP框架的基本功能 .

Spring提供了4种类型的AOP支持:

  • 基于代理的经典Spring AOP
  • 纯POJO切面
  • @AspectJ注解驱动的切面
  • 注入式AspectJ切面(适用与spring各版本)

前三种都是Spring AOP实现的变体 , Spring AOP构建在动态代理的基础之上 , 因此 , Spring对AOP的支持局限于方法拦截 .

借助Spring的aop命名空间 , 可以将纯POJO转换成切面 , 实际上 , 这些POJO只是提供了满足切点条件时所要调用的方法 , 但是这种技术需要XML配置 , 但这的确是声明式的将对象转换为切面的简便方式 .

Spring借鉴了AspectJ的切面 , 以提供注解驱动的AOP , 本质上 , 他依然是Spring基于代理的AOP , 但是编程模式几乎与编写成熟的AspectJ注解切面完全一致 , 这种AOP风格的好处在于可以不使用XML来完成功能 .

如果AOP需求超过了简单的方法调用 , 例如构造器或者属性拦截 , 那么需要考虑使用AspectJ来实现切面 , 这种情况下 , 注入式AspectJ切面能够将值注入到AspectJ驱动的切面中 .

Spring AOP 框架的关键知识


Spring 所创建的通知都是用标准的 Java 类编写的。这样的话,我们就可以使用与普通 Java 开发一样的集成开发环境( IDE )来开发切面。而且,定义通知所应用的切点通常会使用注解或在 Spring 配置文件里采用 XML 来编写,这两种语法对于 Java 开发者来说都是相当熟悉的。

AspectJ 与之相反。虽然 AspectJ 现在支持基于注解的切面,但 AspectJ 最初是以 Java 语言扩展的方式实现的。这种方式有优点也有缺点。通过特有的 AOP 语言,我们可以获得更强大和细粒度的控制,以及更丰富的 AOP 工具集,但是我们需要额外学习新的工具和语法


Spring在运行时通知对象


通过代理类包裹切面 , Spring在运行时期把切面织入到 Spring 管理的 bean 中 ,
代理类封装目标类 , 并拦截被通知方法的调用 , 再把调用转发给真正的目标bean , 当代理拦截到方法调用时 , 在调用方法之前 , 就会执行切面逻辑 .
直到应用需要被代理的bean时 , Spring才会创建代理对象 , 如果使用的是ApplicationContext的话 , 在ApplicationContext从BeanFactory中加载所有bean的时候 , Spring才会创建被代理的对象 , 因为Spring运行时才创建代理对象 , 所以不需要特殊的编译器来织入Spring AOP的切面 .


Spring只支持方法级别的连接点


因为Spring基于动态代理 , 所以Spring只支持方法连接点 , 这与一些其他的AOP框架是不同的 , 例如AspectJ和 JBoss , 除了方法切点 , 他们还提供了字段和构造器注入 , Spring缺少对字段连接点支持 , 无法让我们创建细粒度的通知 , 例如拦截对象字段的修改 , 而且他不支持构造器连接点 , 所以无法在bean创建时应用通知 .


通过切点来选择连接点

… …

原创粉丝点击