Spring in Action -- AOP篇

1、什么是面向切面编程?

  • 概念:通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2、AOP术语

2.1、通知(Advice)

  • 在AOP术语中,切面的工作被称为通知、
  • Spring切面可以应用5中类型的通知
    • 前置通知(Before):在目标方法被调用之前用通知功能
    • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么。
    • 返回通知(After-returning):在目标方法成功执行之后调用通知
    • 异常通知(After-throwing):在目标方法抛出异常后调用通知。
    • 环绕通知(Aroud):通知包裹了被通知的方法,在被通知的方法调用之前和之后执行自定义的行为。

2.2、切点

  • 如果通知定义切面的‘什么’和‘何时’,那么切点就定义了‘何处’,切点的定义会匹配通知所要织入的一个过着额多个连接点。我们通常使用明确的类和方法名称,或者是利用正则表达式定义所匹配的类和方法名称来指定这些切点。

2.3、连接点

  • 连接点是在应用执行过程中能够插入切面的一个点。我们的应用也有数千计时的时机应用通知,
  • 这个点可以是在调用方法时、抛出异常时,甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

2.4、切面

  • 切面就是通知和切点的结合。通知和切点共同定义了切面的全部内容 – 它是什么,在合适和何处完成其功能。

3、Spring对AOP的支持

3.1、概念

  • 创建切点来定义切面锁织入的连接点是AOP框架的基本功能。

3.2、 Spring提供了4种类型的AOP支持

  • 基于代理的经典Spring AOP(笨重和过于复杂,基本弃用)
  • 纯POJO切面(需要配置XML)
  • @AspectJ注解驱动的切面(基于注解)
  • 注入式AspectJ切面(适用于Spring各版本)
  • 前三种都是Spring AOP 实现的辩题,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。

3.3、Spring在运行时通知对象

  • Spring在运行期把切面织入到Spring管理的Bean中,并在代理类中包裹切面以及封装目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。
  • 当代理拦截到方法调用时,再调用目标bean发方法之前,会执行切面逻辑
    在这里插入图片描述
  • 只有在应用需要被代理的bean时,Spring才创建代理对象,所以我们不需要特殊的编译器来织入Spring AOP的切面。

3.4、Spring只支持方法级别的连接点

  • 因为Spring基于动态代理,所以Spring只支持方法连接点。
  • 而AspectJ和JBoss,除了方法切点,它们还提供了字段和构造器接入点。
  • Spring缺少对字段连接点的支持,无法让我们创建细粒度的通知,例如拦截对象字段的修改。

4、通过切点来选择连接点

  • 切点用于准确定位应该在什么店应用切面的通知,通知和其诶单是切面的最基本元素。
    在这里插入图片描述
  • 注意只有execution指示器是实际执行匹配的,而其他的指示器都是用来限制匹配的。

4.1、编写切点

  • 我们需要有一个主题来定义切面的切点,所以定义一个Performance接口
package concert;

public interface Prformance{
    
    
	public void perform();
}
  • 切点表达式,这个表达社能够设置当perform()方法执行时触发通知的调用。
    在这里插入图片描述
  • 假设我们需要匹配的切点仅匹配concert包。可以使用within()指示器来限制匹配。
  • &&代表and 、|| 代表 or、!代表 not 、而在xml中特殊字符有特殊含义,所以我们在xml中还是使用and等
    在这里插入图片描述

4.2、在切点中选择bean

  • Spring还引入了一个新的bean()指示器,他允许我们在其诶单表达式中使用bean的ID来标识bean。
  • 我们希望在执行目标方法时应用通知,但限定bean的ID为woodstock。
    在这里插入图片描述
    在这里插入图片描述

5、使用注解创建切面

5.1、定义切面

  • AspectJ面向注解的模型可以非常简便的通过少量注解把任意类转变为切面。
    在这里插入图片描述
  • 这里使用注解来声明通知方法,且在通知注解里面表明切点目标方法
  • AspectJ提供了五个注解来定义通知。
    在这里插入图片描述
  • 可以看到上面切面中我们给每个注解都定义了一个切点表达式作为他的值,而有些时重复的,那么我们可以使用@Pointcut注解在一个AspectJ切面内定义可重用的切点,
    在这里插入图片描述
  • 这里注意的是虽然用@AspectJ表明是一个切面,但是它依旧是一个POJO,我们能够像其他类一样调用它的方法,以及进行单元测试,也可以装配为Spring中的bean
    在这里插入图片描述
  • 如果只是定义为Spring容器里面的一个bean,即使该类上面标注了@AspectJ注解,但它也不会被视为切面,这些注解不会被解析,也不会创建将其转换为切面的代理。
  • 我们需要在配置类上面通过视同@EnableAspectJ-AutoProxy注解启用自动代理功能。
    在这里插入图片描述
  • 假如你在Spring中要使用XML来装配bean的话,那么需要使用Spring aop命名空间中的<aop:aspectj-autoproxy>元素。
    在这里插入图片描述
  • 这样无论你是使用JavaConfig还是XML,AspectJ自动代理都会为使用@AspectJ注解的bean撞见一个代理,这个代理会围绕着所有该切面的切点所匹配的bean。

5.2、创建环绕通知。

  • 环绕溶质是最为强大的通知类型。
  • 它能够让你所编写的逻辑将被通知的目标方法完全包装起来。
    在这里插入图片描述
  • @Aroud注解表明,watchPerformance()方法回座位performance()切点的环绕通知。
  • 它必须接受ProceedingJoinPoint作为参数,在通知中通过它来调用被通知的方法时,它需要调用proceed()方法。如果不调动可能会阻塞被通知方法的调用。

5.3、处理通知中的参数。

在这里插入图片描述

  • 像之前使用@pointcut注解定义命名切点,但是不同的是在切点声明了要提供给通知方法的参数。
    在这里插入图片描述
  • 注意的是args(trackNumber)限定符,它表明传递给playTrack()方法的int类型参数也会传递到通知中去。
  • 切点定义中的参数与切点方法中的参数名称是一样的,这样就完成了从命名切点到通知方法的参数转移。
  • 方法包装仅仅是切面所能实现的功能之一。

5.4、通过注解引入新功能。

  • 利用被称为引入的AOP概念,切面可以为Spring bean添加新方法。
    在这里插入图片描述
  • 当引入接口的方法被调用时,代理会把此调用委托给实现了新接口的其他对象。实际上,一个bean的实现被拆分到多个类中。
  • 验证,引入一个Encoreable接口。
    在这里插入图片描述
  • 创建一个新的切面。没有提供前置、后置或者环绕等通知。而是通过@DeclareParents注解,将Encoreable接口引入到Perormance bean中。
    在这里插入图片描述
    在这里插入图片描述
  • 注意的是我们需要在Spring应用中将EncoreableIntroducer声明为一个bean
<bean class = "concert.EncoreableIntroducer">
  • Spring 的自动代理机制将会获取到它的声明,当Spring发现一个bean使用了@Aspect注解是,Spring就会创建一个代理,然后叫调用委托给被代理的bean或被引入的实现,这取决于调用的方法属于被代理的bean还是属于被引入的接口。

6、在XML中声明切面。

  • 基于注解的配置要由于基于java的配置,基于java的配置要由于基于XML的配置,如果要声明切面,但是又不能为通知类添加注解,就要使用XML配置。
    在这里插入图片描述
    在这里插入图片描述
  • 把之前的类去掉全部注解
    在这里插入图片描述

6.1、声明前置和后置通知

在这里插入图片描述

  • 大多数AOP配置元素必须在<aop:config>元素的上下文内使用。这条规则有几种例外场景,但是吧bean声明为一个切面时,我们总是从<aop:config>开始配置的。
  • 在该元素内,我们可以声明一个或多个通知其、切面或者切点。
  • 在通知注解中(<aop:before>)里使用methid属性声明方法,用pointcut声明切点。

在这里插入图片描述

  • 在所有的通知元素中,pointcut属性定义了通知所应用的切点,它的值是使用AspectJ切点表达式语法所定义的切点。
  • 使用<aop:pointcut>定义命名公共切点
    在这里插入图片描述

6.2、声明环绕通知。

  • 还原watjcPerformance()方法。
    在这里插入图片描述
  • 利用<aop:around>元素在XML中声明环绕通知。
    在这里插入图片描述

6.3、为通知传递参数。

  • 同理移除掉之前TrackCounter上的所有@AspectJ注解。
    在这里插入图片描述
  • 用XML将TrackCounter配置为参数化的切面。
    在这里插入图片描述

6.4、通过切面引入新功能

  • 前面的在借助Aspect的@DeclareParents注解为被通知的方法引入新的方法,我们也可以在XML中用<aop:declare-parents>元素。
    在这里插入图片描述
  • 类型匹配Performance接口(由types-matching属性指定)的那些bean在父类结构中会增加Encoreable接口(由implement-interface属性指定)。
  • 我们使用default-impl属性全限定类名来显示指定Encoreable的实现。或者使用delegate-ref属性来标识。
    在这里插入图片描述
  • 这里引用了一个bean,所以我们要创建一个bean,使用default-impl来世界标识委托和间接使用delegate-ref的区别在于后者是Spring bean,它本身可以被注入、通知或使用其他的Spring配置。
    在这里插入图片描述

7、总结

  • 作用:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  • 使用:可以使用注解的方式或者XML编写的方式来使用AOP。

猜你喜欢

转载自blog.csdn.net/JISOOLUO/article/details/105707380
今日推荐