Skip to content

Spring基础-Spring核心之面向切面编程(AOP) #26

Description

@cyjxiaozhi

一、引入
1.Spring 框架通过定义切面, 通过拦截切点实现了不同业务模块的解耦,这个就叫面向切面编程 - Aspect Oriented Programming (AOP)
2为什么@aspect注解使用的是aspectj的jar包呢?这就引出了Aspect4J和Spring AOP的历史渊源,只有理解了Aspect4J和Spring的渊源才能理解有些注解上的兼容设计
3.如何支持更多拦截方式来实现解耦, 以满足更多场景需求呢? 这就是@around, @pointcut... 等的设计
4.那么Spring框架又是如何实现AOP的呢? 这就引入代理技术,分静态代理和动态代理,动态代理又包含JDK代理和CGLIB代理等本节将在此基础上进一步解读AOP的含义以及AOP的使用方式;

二、如何理解AOP
image
image

三、AOP术语
连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,Spring只支持方法执行连接点,在AOP中表示为在哪里干

切入点(Pointcut): 选择一组相关连接点的模式,即可以认为连接点的集合,Spring支持perl5正则表达式和AspectJ切入点模式,Spring默认使用AspectJ语法,在AOP中表示为在哪里干的集合;

通知(Advice):在连接点上执行的行为,通知提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置通知(before advice)、后置通知(after advice)、环绕通知(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以环绕连接点的拦截器链织入通知;在AOP中表示为干什么;

方面/切面(Aspect):横切关注点的模块化,比如上边提到的日志组件。可以认为是通知、引入和切入点的组合;在Spring中可以使用Schema和@AspectJ方式进行组织实现;在AOP中表示为在哪干和干什么集合;

引入(inter-type declaration):也称为内部类型声明,为已有的类添加额外新的字段或方法,Spring允许引入新的接口(必须对应一个实现)到所有被代理对象(目标对象), 在AOP中表示为干什么(引入什么);目标对象(Target Object):需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被通知的对象,从而也可称为被通知对象;由于Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象,在AOP中表示为对谁干

织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。在AOP中表示为怎么实现的;

AOP代理(AOP Proxy):AOP框架使用代理模式创建的对象,从而实现在连接点处插入通知(即应用切面),就是通过代理来对目标对象应用切面。在Spring中,AOP代理可以用JDK动态代理或CGLIB代理实现,而通过拦截器模型应用切面。在AOP中表示为怎么实现的一种典型方式;

前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。

环绕通知是最常用的通知类型。和AspectJ一样,Spring提供所有类型的通知,我们推荐你使用尽可能简单的通知类型来实现需要的功能。例如,如果你只是需要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知,尽管环绕通知也能完成同样的事情。用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。比如,你不需要在JoinPoint上调用用于环绕通知的proceed()方法,就不会有调用的问题。
image

三、Spring AOP和AspectJ是什么关系
3.1 首先AspectJ是什么?
AspectJ是一个java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)
可以这样说AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言,更幸运的是,AspectJ与java程序完全兼容,几乎是无缝关联,因此对于有java编程基础的工程师,上手和使用都非常容易。

3.2 Spring AOP和AspectJ是什么关系?
Spring AOP(面向切面编程)和 AspectJ 都是 Java 中用于实现面向切面编程(AOP)的框架,但它们有不同的实现方式和应用场景。下面是它们的关系和区别。

  1. Spring AOP 与 AspectJ 的关系
    • Spring AOP 是 Spring 框架实现的一种轻量级的 AOP(面向切面编程)框架,它专注于方法级的横切关注点(例如:事务管理、日志记录、安全性等)。它是基于代理模式实现的,通常使用 JDK 动态代理 或 CGLIB 字节码生成 来创建代理对象。
    • AspectJ 是一个功能强大的 AOP 框架,它支持更为丰富的 AOP 特性,尤其是在类级别、字段、构造方法等切入点上提供了更强大的支持。AspectJ 是一个独立的 AOP 框架,除了通过代理的方式,它还支持通过字节码编织的方式实现 AOP。它的功能比 Spring AOP 更强大,并且支持更多的切入点类型。

Spring AOP 和 AspectJ 的结合
• Spring AOP 可以与 AspectJ 配合使用。在 Spring 中,Spring AOP 是基于 AspectJ 的一部分,但它实现了一个简化的模型,适合应用于 Spring 框架的常见场景。Spring AOP 只支持方法级别的切面(例如:前置通知、后置通知等),而 AspectJ 提供了更多切入点的选择(如构造方法、字段等)。
• 如果你想使用 Spring AOP 的简单特性,但又需要 AspectJ 的更强大功能,你可以将 AspectJ 和 Spring AOP 结合起来使用。例如,Spring 支持通过配置启用 AspectJ 编织,允许你使用 AspectJ 的强大功能(如更复杂的切点表达式)。

  1. 主要区别
    • 功能强度:
    • Spring AOP:实现了 AOP 的基本功能,适用于大多数 Spring 项目。它的优势是轻量级、易于配置,集成非常方便,适用于常见的横切关注点(例如日志、事务等)。
    • AspectJ:比 Spring AOP 提供更广泛的功能,支持更复杂的切点和更丰富的通知类型。AspectJ 不仅支持方法级别的切点,还支持字段、构造函数等切入点。
    • 实现方式:
    • Spring AOP:通过代理模式(JDK 动态代理或 CGLIB 字节码生成)实现,主要关注于方法级别的增强。Spring AOP 的切入点表达式较为简单,基于方法的签名来进行匹配。
    • AspectJ:支持编译时、类加载时、运行时的织入方式。AspectJ 使用自己的语言(AspectJ 语言)定义切面,可以进行更细粒度的切入,甚至支持字段、构造函数等。
    • 使用方式:
    • Spring AOP:通过 Spring 配置文件或注解(如 @aspect, @before, @after 等)来配置切面和通知。
    • AspectJ:可以通过 AspectJ 注解(如 @aspect, @before, @after, @around)以及 AspectJ 编织工具进行更强大的控制。AspectJ 还可以通过特殊的编译插件将切面编织进字节码。

  2. 配置方式对比

Spring AOP

Spring AOP 主要通过代理模式来实现:

或者通过注解方式:

@aspect
@component
public class MyAspect {

@Before("execution(* com.example.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
    System.out.println("Before method: " + joinPoint.getSignature().getName());
}

}

AspectJ

AspectJ 配置通常通过切面语言和编织过程来实现:

@aspect
public class MyAspect {

@Before("execution(* com.example.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
    System.out.println("Before method: " + joinPoint.getSignature().getName());
}

}

你可以在编译时使用 AspectJ 编织工具(例如:ajc)将切面编织到字节码中。

  1. 使用场景
    • Spring AOP 适用于大部分 Spring 项目,尤其是在需要与 Spring 框架紧密集成的情况下,例如:事务管理、日志记录、安全性等典型的横切关注点。
    • AspectJ 适用于需要更多切点灵活性和细粒度控制的场景,特别是对于复杂的企业级应用程序,或者需要进行跨模块切面编程的场合。它的能力远超 Spring AOP,但也因此相对复杂。

AspectJ注解方式
基于XML的声明式AspectJ存在一些不足,需要在Spring配置文件配置大量的代码信息,为了解决这个问题,Spring 使用了@AspectJ框架为AOP的实现提供了一套注解。
image

四、AOP使用问题
1.切入点(pointcut)的申明规则?
image

image

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions