Spring 闯关指南:AOP

  • Post author:
  • Post category:其他


Spring 官方文档中指出:

面向切面编程通过提供另一种思考程序结构的方式,对面向对象编程(OOP)进行了补充。在 OOP 中,模块化的关键单元是类,而在 AOP 中,模块化的单元是切面。切面支持跨多个类型和对象的关注点的模块化。

如果单纯的将类看作一种纵向,从类到字段或者方法。那么切面可以看作一种横向,囊括多个类的方法。如此“纵横交错”,在面临复杂问题也能游刃有余。在日常使用中,我们经常自定义切面,用 AOP 补充对 OOP 的使用。



AOP 术语

理解术语,这很重要。


Aspect

(切面):一个跨多个类的关注点的模块化;


Join point

(连接点): 程序执行过程中的一个点,如方法的执行或异常的处理;


Advice

(建议):切面在特定连接点采取的操作;其类型有“ around”,“before” 和 “after” 等;


Pointcut

(切入点):匹配连接点的谓词。Advice 是与切入点相关联,并在与切入点匹配的任何连接点上运行。


introduction

: 代表一个类声明其他方法或字段。允许向任何目标对象引入新的接口(以及相应的实现)。


目标对象

:由一个或多个切面建议的对象,也称为“建议对象”。因为 spring aop 是通过运行时代理实现的,所以这个对象总是一个代理对象。


AOP代理

:由 AOP 框架创建的一个对象,用于实现切面契约(advice 方法执行等)。在 Spring 框架中,AOP 代理是 JDK 动态代理或 CGLIB 代理。


编织

:将切面与其他应用程序类型或对象连接起来,以创建一个建议对象。这可以在编译时、加载时或运行时完成。Spring AOP 在运行时执行编织。

计算机领域有一句话:“

计算机的所有问题,都可以通过加入一个中间层解决

”。我认为这句话的适用范围比它想象的还要广。在这里,切入点便可看作一个中间层,它连接了连接点和建议。中间层的设计,符合解耦思想,也能够更加灵活地组合。

Spring 的建议类型有五种,它代表了建议如何去影响目标对象。

  • Before advice:在连接点之前运行但不能阻止执行流继续到连接点的建议(除非抛出异常)。
  • After returning advice: 连接点正常完成后运行的建议(如果方法返回时没有抛出异常)。
  • After throwing advice: 如果方法通过抛出异常退出,则要运行的建议。
  • After(finally) advice: 无论连接点以何种方式退出(正常或异常返回),都要运行的建议
  • Around advice: 围绕连接点(如方法调用)的建议。这是最有力的建议。Around 建议可以在方法调用前后执行自定义行为。

Spring 官方文档建议使用最小的建议类型来实现所需的行为。也就是说如果只需要使用方法的返回值更新缓存,那么最好实现 After returning 的建议类型。尽管 around 的建议也可以完成相同的任务。但使用最具体的 advice 类型提供了一个更简单的编程模型,出错的可能性更小。

Spring AOP 默认将 JDK 动态代理用于 AOP 代理,这使得可以代理任何接口。Spring AOP 也可以使用 CGLIB 代理。默认情况下,如果业务对象未实现接口,则使用 CGLIB 。


掌握 Spring AOP 是基于代理的这一事实非常重要。



Spring AOP

明白了上述的一些概念,来看看 Spring 中是如何表示上面的术语的:


  • Joinpoint

    接口;

  • Pointcut

    接口;

  • Advice

    接口:每个

    Advice

    是一个 Spring bean,一个 advice 实例可以被所有目标对象共享,也可以对每个目标对象唯一。

  • Advised

    接口:从 Spring 中获得的任何 AOP 代理都可以强制被转换到这个接口。

  • Advisor

    接口: 在 Spring 中,

    Advisor

    是一个切面,它包含一个与切入点表达式关联的

    Advice

    对象。

  • ProxyFactoryBean



    ProxyFactory

    类:用于创建 AOP 代理,前者用于 Spring 管理 Bean 的代理的创建,后者用于编程时创建。

除了特殊情况,任何

Advisor

都可以使用任何

Advice



org.springframework.aop.support.DefaultPointcutAdvisor

是最常用的 advisor 类。它可以与

MethodInterceptor



BeforeAdvice



ThrowsAdvice

一起使用。

基于前文所提到的概念,目标对象一般是作为 spring 管理的 bean,而代理对象则是 spring 使用代理(基于 JDK 或者 CGLIB)包装原生 bean 而生成的对象。advise 在 spring 中是拦截器。连接点的执行单元在 spring 中只能是方法,spring aop 并未实现基于字段的拦截。

advisor 则是 Spring 提供的,能够有效的将切入点与 advise 连接起来。所以,上述抽象类所做的工作是通过 advisor 或直接通过 interceptor 来包装 bean。理解这一点很重要。

除此之外,我们可以在同一个 AOP 代理中混合使用 Spring 中的

advisor



advice

类型。例如,我们可以在一个代理配置中使用

around advice

,

throws advice



before advice

的拦截。Spring 会自动创建必要的

拦截器链



在容器中使用 AOP

上面介绍了 Spring aop 中几个主要对象,现在,我们需要知道它们是如何被组织运行的。

在 AOP 中,与我们直接接触是代理对象,所以从代理对象的创建过程入手。

前文有提到 AOP 代理由

ProxyFactoryBean



ProxyFactory

类来完成创建工作。可具体的创建时机呢?

结合我们以往的编程经验以及 AOP 代理的特性,可以判断出代理对象需要在对象实例化后完成创建。所以,它的创建过程应该是通过实现

BeanPostProcessor

(Bean 后置处理器) 接口来完成的。查看该接口的实现类,找到与代理有关的实现:


  • AbstractAutoProxyCreator

  • AbstractAdvisingBeanPostProcessor

  • AbstractAdvisorAutoProxyCreator

上面这几个类都是抽象类,作用是

使用 AOP 代理包装每一个合格的 Bean,在调用 Bean 的方法之前,委托给特定的拦截器

。通过分析代码实现以及其提供给子类的可重写方法,我们总结下作用。


AbstractAutoProxyCreator

:它为所有代理对象提供共享的代理拦截器,如果有大量的 bean ,需要使用类似的代理进行包装,即委派给相同的拦截器,我们可以注册单个这样的后置处理器。

子类可以应用任何策略来决定是否代理 Bean,例如

BeanNameAutoProxyCreator

通过名称代理 bean。


AbstractAdvisingBeanPostProcessor

:将 advisor 应用到特定的 bean。比如常见的

Async

注解的支持就是通过继承该抽象类来实现的。


AbstractAdvisorAutoProxyCreator

:它作为

AbstractAutoProxyCreator

的子类,能够基于检测到的 advisor 为 bean 创建代理。

所以,在容器中,代理对象会通过后置处理器创建,并且我们可以定义自定义拦截器( advice )。更详细的创建过程,可以去参考它们的子类。


Spring AOP 是 Spring 利用自身可拓展的特性实现的



写在最后

一直以来, aop 是 Spring 中一个复杂且难理解的特性,但只要掌握对方法,从源头上开始分析,我们仍然能够掌握它。认为一个技术好与不好有着清楚的判断标准,但确定一个设计的好与坏,只有在使用时才能顿悟。我们在设计基于 aop 解决问题的方案时,仍然很难弄明白如何设计才是最合适。所以,希望理解这些基础概念会对这些困惑有所帮助。

大多数人提到:“能够将复杂的东西说简单才是真正的明白了”。但在自我学习时,不应该只接触优秀的人所总结出的简单知识。学习知识复杂的一面仍然对我们很重要,因为学习的过程可以加以复制,重复使用。并且,也不是每一次都会有前辈为我们总结好方方面面,总有一天,我们也会作为领路人走在前面。届时,你的大脑将是你唯一的支撑。


这是 Spring 闯关指南系列 的第三篇文章,如果你觉得我的文章还不错,并对后续文章感兴趣的话,可以通过扫描下方二维码关注我的公众号。谢谢!

在这里插入图片描述



版权声明:本文为ai_xao原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。