软件系统中的一些功能需要应用到应用程序的多个地方,如日志、安全、缓存和声明式事务。
散布于应用中多处的功能被称为横切关注点,这些横切关注点从逻辑上与应用的业务逻辑相分离,但往往会直接嵌入到业务逻辑之中,把这些横切关注点从业务逻辑分离正是面向切面编程要解决的问题。
- 继承委托使对象体系变得复杂,切面提供了另一种更加清晰简洁的方案。
- 每个关注点都集中在一个地方,不用分散在代码中。
- 服务模块更简洁,因为他们只关注核心功能。
什么是面向切面编程
通知(Advice)
切面要完成的工作(什么),何时完成这个工作(何时)。
- before:前置通知
- After:后置通知
- After-returning:目标方法执行之后执行通知
- After-throwing:在目标方法抛出异常后执行通知
- Around:包裹了目标方法,在方法执行前和调用后执行通知
连接点(Join point)
应用执行过程中能够插入切面的点,切面可以利用这些点插入到正常业务逻辑之中,并添加新的行为。
切点(Poincut)
一个切面不需要通知应用的所有连接点,切点有助于缩小切面所通知的连接点的范围(何处)。
切面(Aspect)
切面是通知和切点的结合,通知和切点共同定义了切面的全部内容——切面要完成什么功能,在何时何处完成。
引入(Introduction)
向现有类添加新的方法或属性。
织入(Weaving)
织入把切面应用到目标对象并创建新的代理对象,切面在指定的连接点织入到目标对象,可在多个可选的对象声明周期里织入:
- 编译期:需要特殊的编译器,如 AspectJ 的织入编译器
- 类加载期:目标类加载到 JVM 时织入,需要特殊的类加载器(ClassLoader),增强目标类的字节码
- 运行期:运行时,AOP 容器动态的为目标对象创建代理对象,如 Spring AOP
Spring 对 aop 的支持
spring 提供了 4 种 aop 支持
- 基于代理的经典 spring aop
- 纯 POJO 切面(POJO:Plain ordinary java object - java beans)
- @AspectJ 注解驱动的切面
- 注入式 AspectJ 切面
spring aop 构建在动态代理之上,所有spring 对 aop 的支持局限于方法拦截。
spring 在运行时通知对象
- spring 代理封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的 bean,当代理拦截到方法调用时,在调用目标 bean 方法之前,会执行切面逻辑。
- spring运行时才创建代理对象,所以不需要特殊的编译器织入 spring aop 切面。
AspectJ 最初是以 java 语言扩展的方式实现的,有自己的编译器,除方法切点外,还提供了字段(拦截字段的修改)和构造器(构造器调用时)接入点。(JBoss 也提供了同样的粒度)
spring aop 与 AspectJ 项目
- spring aop 与 AspectJ 项目间有大量的协作,Spring 对 aop 的支持在很多方面借鉴了 AspectJ 项目(注解驱动、切点表达式语言的子集 ...)
- spring 的 AspectJ 自动代理仅仅使用 @AspectJ 作为创建切面的指定,切面依然是基于代理的。这意味着尽管使用的是 @AspectJ 注解,我们还是限定于代理方法的调用,如果想利用 AspectJ 的所有能力,我们必须在运行时使用 AspectJ 并且不依赖 Spring 来创建基于代理的切面。
通过切点选择连接器
在 spring aop 中,要使用 AspectJ 的切点表达式语言来定义切点,spring 仅支持 AspectJ 切点指示器(pointcut designator)的一个子集。
AspectJ 指示器
- arg() 限制参数
- @args() 限制参数注解
- execution() 匹配连接点
- this() 限制代理的 bean 引用的类型
- target 限制目标对象类型
- @target() 限制目标对象类型,同时类型需要有特定注解
- within() 限制类型,包
- @within() ...?
- annotation 限定注解
- bean() spring 提供的指示器,用于限定指定 bean
指示器使用
- "(..)" 表示不限定参数,"*" 任意返回值类型
-
&&(and)、||(or)、!(not)
image.png
image.png
网友评论