Spring 闯关指南:AOP

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 对象。
  • ProxyFactoryBeanProxyFactory 类:用于创建 AOP 代理,前者用于 Spring 管理 Bean 的代理的创建,后者用于编程时创建。

除了特殊情况,任何 Advisor 都可以使用任何 Adviceorg.springframework.aop.support.DefaultPointcutAdvisor 是最常用的 advisor 类。它可以与 MethodInterceptorBeforeAdviceThrowsAdvice一起使用。

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

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

除此之外,我们可以在同一个 AOP 代理中混合使用 Spring 中的 advisoradvice 类型。例如,我们可以在一个代理配置中使用 around advice, throws advicebefore advice 的拦截。Spring 会自动创建必要的拦截器链

在容器中使用 AOP

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

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

前文有提到 AOP 代理由 ProxyFactoryBeanProxyFactory 类来完成创建工作。可具体的创建时机呢?
结合我们以往的编程经验以及 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 闯关指南系列 的第三篇文章,如果你觉得我的文章还不错,并对后续文章感兴趣的话,可以通过扫描下方二维码关注我的公众号。谢谢!
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值