Spring与AOP

Spring与AOP

一、AOP概述

(1)AOP简介

  • AOP(Aspect Orient Programming),面向切面编程,是面向对象编程 OOP 的一种补充。面向对象编程是从静态角度考虑程序的结构,而面向切面编程是从动态角度考虑程序运行过程。AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理。
  • 面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志等。

(2)AOP编程术语

  • 切面(Aspect):切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面有通知与顾问。实际就是对主业务逻辑的一种增强。
  • 织入(Weaving):织入是指将切面代码插入到目标对象的过程。上例中MyInvocationHandler类中的invoke()方法完成的工作,就可以称为织入。
  • 连接点(JoinPoint):连接点指可以被切面织入的方法。通常业务接口中的方法均为连接点。
  • 切入点(Pointcut):切入点指切面具体织入的方法。在StudentServiceImpl 类中,若 doSome()将被增强,而doOther()不被增强,则 doSome()为切入点,而 doOther()仅为连接点。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
  • 目标对象(Target):目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象 。 上 例 中 的StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。
  • 通知(Advice):通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。切入点定义切入的位置,通知定义切入的时间。
  • 顾问(Advisor):顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。

二、通知Advice

  • 通知(Advice),切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。常用通知有:前置通知、后置通知、环绕通知、异常处理通知。

(1)通知的用法步骤

  • 定义目标类

    就是定义之前的普通 Bean 类,也就是即将被增强的 Bean 类。

  • 定义通知类

    通知类是指,实现了相应通知类型接口的类。当前,实现了这些接口,就要实现这些接口中的方法,而这些方法的执行,则是根据不同类型的通知,其执行时机不同。
    1,前置通知:在目标方法执行之前执行
    2,后置通知:在目标方法执行之后执行
    3,环绕通知:在目标方法执行之前与之后均执行
    4,异常处理通知:在目标方法执行过程中,若发生指定异常,则执行通知中的方法

  • 注册目标类以及切面

    即在 Spring 配置文件中注册目标对象 Bean、通知对象 Bean。
    这里写图片描述
    这里写图片描述

  • 注册代理工厂 Bean 类对象 ProxyFactoryBean
    这里写图片描述
    这里的代理使用的是 ProxyFactoryBean 类。代理对象的配置,是与 JDK 的 Proxy 代理参数是一致的,都需要指定三部分:目标类,接口,切面。
    name=“target” ref=“目标对象 Bean 的 id”
    指定目标对象的 Bean 的 id。也可写为如下形式:
    1,name=“targetName” value=“目标对象 Bean 的 id”
    2,property name=“proxyInterfaces” value=“接口全限定性名”
    设置目标对象所实现的业务接口,要求给出接口的全既定性类名。此属性可以不进行设置,因为打开 ProxyFactoryBean 的源码,可以看到其有个自动检测目标类的所有接口属性autodetectInterfaces,默认值为 true。即不设置也可以自动检测到。当然,此时使用的是 jdk的 Proxy 动态代理。
    如果目标对象没有实现业务接口,则可以不设置。此时使用的是 CGLIB 动态代理。

  • 客户端访问动态代理对象

    客户端访问的是动态代理对象,而非原目标对象。因为代理对象可以将交叉业务逻辑按照通知类型,动态的织入到目标对象的执行中。
    这里写图片描述

  • 在容器中的整体配置
    这里写图片描述

(2)通知详解

  • 前置通知 MethodBeforeAdvice
    定义前置通知,需要实现 MethodBeforeAdvice 接口。该接口中有一个方法 before(),会在目标方法执行之前执行。前置通知的特点:
    1,在目标方法执行之前先执行。
    2,不改变目标方法的执行流程,前置通知代码不能阻止目标方法执行。
    3,不改变目标方法执行的结果。
    4,对于测试类,需要注意,从容器中获取的是代理对象,而非目标对象。

  • 后置通知 AfterReturningAdvice
    定义后置通知,需要实现接口AfterReturningAdvice。该接口中有一个方法afterReturning(),会在目标方法执行之后执行。后置通知的特点:
    1,在目标方法执行之后执行。
    2,不改变目标方法的执行流程,后置通知代码不能阻止目标方法执行。
    3,不改变目标方法执行的结果。

  • 环绕通知 MethodInterceptor
    定义环绕通知,需要实现 MethodInterceptor 接口。环绕通知,也叫方法拦截器,可以在目标方法调用之前及之后做处理,可以改变目标方法的返回值,也可以改变程序执行流程。注意, org.aopalliance.intercept.MethodInterceptor 才是需要的包。

  • 异常通知 ThrowsAdvice

    定义异常通知,需要实现 ThrowsAdvice 接口。该接口的主要作用是,在目标方法抛出异常后,根据异常的不同做出相应的处理。当该接口处理完异常后,会简单地将异常再次抛出给目标方法。

    不过,这个接口较为特殊,从形式上看,该接口中没有必须要实现的方法。但,这个接口却确实有必须要实现的方法 afterThrowing()。这个方法重载了四种形式。由于使用时,一般只使用其中一种,若要都定义到接口中,则势必要使程序员在使用时必须要实现这四个方法。这是很麻烦的。所以就将该接口定义为了标识接口(没有方法的接口)。

(3)通知的其他用法

  • 给目标方法织入多个切面:若要给目标方法织入多个切面,则需要在配置代理对象的切面属性时,设定为 list。
    这里写图片描述

  • 无接口的 CGLIB 代理生成:若不存在接口,则 ProxyFactoryBean 会自动采用 CGLIB 方式生成动态代理。

  • 有接口的 CGLIB 代理生成- proxyTargetClass 属性:若存在接口,但又需要使用 CGLIB 生成代理对象,此时,只需要在配置文件中增加一个
    proxyTargetClass 属性设置,用于指定强制使用 CGLIB 代理机制。
    这里写图片描述
    也可指定 optimize(优化)的值为 true,强制使用 CGLIB 代理机制。
    这里写图片描述

三、顾问Advisor

  • 通知(Advice)是 Spring 提供的一种切面(Aspect)。但其功能过于简单:只能将切面织入到目标类的所有目标方法中,无法完成将切面织入到指定目标方法中。
  • 顾问(Advisor)是 Spring 提供的另一种切面。其可以完成更为复杂的切面织入功能。PointcutAdvisor 是顾问的一种,可以指定具体的切入点。顾问将通知进行了包装,会根据不同的通知类型,在不同的时间点,将切面织入到不同的切入点。
  • PointcutAdvisor 接口有两个较为常用的实现类:
    1,NameMatchMethodPointcutAdvisor 名称匹配方法切入点顾问
    2,RegexpMethodPointcutAdvisor 正则表达式匹配方法切入点顾问

(1)名称匹配方法切入点顾问

  • NameMatchMethodPointcutAdvisor,即名称匹配方法切入点顾问。容器可根据配置文件中指定的方法名来设置切入点。
    这里写图片描述
    对于切入点的指定,有多种方式:
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述

(2)正则表达式方法切入点顾问

  • RegexpMethodPointcutAdvisor,即正则表达式方法顾问。容器可根据正则表达式来设置切入点。注意,与正则表达式进行匹配的对象是接口中的方法名,而非目标类(接口的实现类)的方法名。
    这里写图片描述
    顾问的其他写法:
    这里写图片描述
    这里写图片描述

四、自动代理生成器

  • 前面代码中所使用的代理对象,均是由 ProxyFactoryBean 代理工具类生成的。而该代理工具类存在着如下缺点:
    1,一个代理对象只能代理一个 Bean,即如果有两个 Bean 同时都要织入同一个切面,这时,不仅要配置这两个 Bean,即两个目标对象,同时还要配置两个代理对象。
    2,在客户类中获取 Bean 时,使用的是代理类的 id,而非我们定义的目标对象 Bean 的 id。我们真正想要执行的应该是目标对象。从形式上看,不符合正常的逻辑。Spring 提供了自动代理生成器,用于解决ProxyFactoryBean 的问题。常用的自动代理生成器有两个:
     默认 advisor 自动代理生成器
     Bean 名称自动代理生成器
  • 需要注意的是,自动代理生成器均继承自 Bean 后处理器 BeanPostProcessor。容器中所有 Bean 在初始化时均会自动执行 Bean 后处理器中的方法,故其无需 id 属性。所以自动代理生成器的 Bean 也没有 id 属性,客户类直接使用目标对象 bean 的 id。

(1)默认 advisor 自动代理生成器

  • DefaultAdvisorAutoProxyCreator 代理的生成方式是,将所有的目标对象与 Advisor 自动结合,生成代理对象。无需给生成器做任何的注入配置。注意,只能与 Advisor 配合使用。这种代理的配置很简单,如下:
    这里写图片描述

(2)Bean 名称自动代理生成器

  • DefaultAdvisorAutoProxyCreator 会为每一个目标对象织入所有匹配的 Advisor,不具有选择性,且切面只能是顾问 Advisor。而 BeanNameAutoProxyCreator 的代理生成方式是,根据bean 的 id,来为符合相应名称的类生成相应代理对象,且切面既可以是顾问 Advisor 又可以是通知 Advice。配置文件:
    这里写图片描述
    这里写图片描述
    beanNames:指定要增强的目标类的 id
    interceptorNames:指定切面。可以是顾问 Advisor,也可以是通知Advice。
    这里写图片描述
    对于自动代理生成器在配置时,需要注意,即使只有一个目标对象,属性也为 beanNames,且对于 Bean 的名称的指定,可以使用通配符*号。
    当然,对于多个 Bean 名称的情况,还可使用list标签的形式。
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述

五、AspectJ 对 AOP 的实现
https://blog.csdn.net/king_cannon_fodder/article/details/80211523

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值