AOP 概念
让我们首先定义一些核心的 AOP 概念和术语。这些术语并非 Spring 特有。遗憾的是,AOP 术语并不是特别直观。然而,如果 Spring 使用自己的术语,会更加令人困惑。
-
切面(Aspect):对跨越多个类的关注点的模块化。事务管理就是企业 Java 应用中横切关注点的一个很好的例子。在 Spring AOP 中,切面通过普通类(基于 Schema 的方法)或使用
@Aspect
注解标记的普通类(@AspectJ 风格)来实现。 -
连接点(Join point):程序执行期间的某个点,例如方法的执行或异常的处理。在 Spring AOP 中,连接点总是代表方法的执行。
-
通知(Advice):切面在特定连接点采取的行动。不同类型的通知包括“环绕通知”、“前置通知”和“后置通知”。(通知类型稍后讨论。)许多 AOP 框架,包括 Spring,将通知建模为拦截器,并在连接点周围维护一个拦截器链。
-
切点(Pointcut):匹配连接点的谓词。通知与切点表达式相关联,并在切点匹配到的任何连接点处运行(例如,执行某个特定名称的方法)。通过切点表达式匹配连接点的概念是 AOP 的核心,Spring 默认使用 AspectJ 的切点表达式语言。
-
引入(Introduction):代表某个类型声明额外的方法或字段。Spring AOP 允许你为任何被通知的对象引入新的接口(以及相应的实现)。例如,你可以使用引入来使一个 bean 实现
IsModified
接口,从而简化缓存。(在 AspectJ 社区中,引入被称为 inter-type declaration。) -
目标对象(Target object):一个被一个或多个切面通知的对象。也称为“被通知对象”(advised object)。由于 Spring AOP 是通过运行时代理实现的,因此这个对象总是一个代理对象。
-
AOP 代理(AOP proxy):由 AOP 框架创建的对象,用于实现切面契约(通知方法执行等)。在 Spring Framework 中,AOP 代理是 JDK 动态代理或 CGLIB 代理。
-
织入(Weaving):将切面与应用程序的其他类型或对象连接起来,以创建被通知的对象。这可以在编译时(例如,使用 AspectJ 编译器)、加载时或运行时完成。Spring AOP 与其他纯 Java AOP 框架一样,在运行时执行织入。
Spring AOP 包括以下通知类型:
-
前置通知(Before advice):在连接点之前运行的通知,但它没有能力阻止执行流进行到连接点(除非它抛出异常)。
-
后置返回通知(After returning advice):在连接点正常完成(例如,方法返回而没有抛出异常)后运行的通知。
-
后置异常通知(After throwing advice):如果方法因抛出异常而退出时运行的通知。
-
后置(最终)通知(After (finally) advice):无论连接点以何种方式退出(正常返回或抛出异常)都运行的通知。
-
环绕通知(Around advice):围绕连接点(例如方法调用)的通知。这是功能最强大的通知类型。环绕通知可以在方法调用之前和之后执行自定义行为。它还负责选择是否继续执行连接点,或者通过返回自己的返回值或抛出异常来缩短被通知方法的执行。
环绕通知是最通用的通知类型。由于 Spring AOP 像 AspectJ 一样提供了全范围的通知类型,我们建议你使用能够实现所需行为的最弱的通知类型。例如,如果你只需要用方法的返回值更新缓存,那么实现一个后置返回通知比环绕通知更好,尽管环绕通知也能实现同样的事情。使用最具体的通知类型提供了更简单的编程模型,减少了出错的可能性。例如,在使用环绕通知时,你不需要在 JoinPoint
上调用 proceed()
方法,因此也不会因为忘记调用它而出错。
所有通知参数都是静态类型的,因此你可以使用适当类型的通知参数(例如,方法执行返回值的类型),而不是 Object
数组。
通过切点匹配连接点的概念是 AOP 的关键,它与仅提供拦截功能的旧技术有所区别。切点使得通知可以独立于面向对象层次结构进行目标定位。例如,你可以将提供声明式事务管理的环绕通知应用于跨越多个对象的方法集(例如服务层中的所有业务操作)。