Spring Aop学习
《Spring in Action》的持续学习中,来到了spring aop,以下是一个简单的梳理:
AOP术语:
通知(advice):切面的工作。
切点(pointcut)。
连接点(join point):应用执行过程中能够插入切面的一个点。这个点可以使调用方法时、抛出异常时、甚至修改一个字短时。
切面(aspect):通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。
织入(weaving):spring aop是在运行时通过动态代理织入的。
五种通知
spring切面可以应用五种类型的通知:
- 前置通知(before):在目标方法被调用之前调用此通知功能。
- 后置通知(after):在目标方法完成之后调用通知,此时不关心方法的输出是什么。
- 返回通知(after-returning):在目标方法成功执行之后调用通知。
- 异常通知(after-throwing):在目标方法抛出异常后调用通知。
- 环绕通知(around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
注意:应用顺序:环绕通知->环绕通知内应用before前置通知->环绕通知全体完毕后应用后置通知。如果环绕通知内有异常,则需要catch,否则应用完after-throwing异常通知后,将会抛出异常;若在环绕通知内已catch,将不再调用异常通知。如果异常通知调用,返回通知自然不用。
注意:应用顺序:环绕通知->环绕通知内应用before前置通知->环绕通知全体完毕后应用后置通知。如果环绕通知内有异常,则需要catch,否则应用完after-throwing异常通知后,将会抛出异常;若在环绕通知内已catch,将不再调用异常通知。如果异常通知调用,返回通知自然不用。
spring借鉴了aspectj的切面,以提供注解驱动的AOP,但本质上依然是基于动态代理的,所以只支持方法级别的连接点。sprign仅支持AspectJ切点指示器的一个子集。
在spring中尝试使用AspectJ其他指示器时,将会抛出IllegalArgument-Exception异常。
原理:
通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。
bean( )指示器:在切点表达式中使用bean的Id来表示bean,使用beanID或bean名称作为参数来限制切点只匹配特定的bean。
使用注解创建切面
AspectJ提供了五个注解来定义通知:
可以使用@Pointcut注解定义命名切点。
@Pointcut注解能够在一个@AspectJ切面内定义可重用的切点。
启用自动代理功能:
javaConfig:@EnableAspectJAutoProxy
xml:<aop:aspectj-autoproxy>
不管是javaConfig还是xml,AspectJ自动代理都会为使用@Aspect注解的bean创建一个代理,这个代理会围绕着所有该切面的切点所匹配的bean。
切面可以访问和使用传递给被通知方法的参数
在切点表达式中使用arg()切点指示器限定匹配。
切点定义中的参数与切点方法中的参数名称一致,通过通知注解和命名切点定义通知,就完成了从命名切点到通知方法的参数转移。
例:
在XML中声明切面
如果没有源码,或者不想将AspectJ注解放进你的代码之中,spring为切面提供了另外一种可选方案:在spring XML配置文件中声明切面。
Spring AOP
spring的aop命名空间中,提供了多个配置元素用来在xml中声明切面。
AOP配置元素 | 用途 |
---|---|
<aop:advisor> |
定义AOP通知器 |
<aop:after> |
定义AOP后置通知 |
<aop:after-returning> |
定义AOP返回通知 |
<aop:after-throwing> |
定义AOP异常通知 |
<aop:around> |
定义AOP环绕通知 |
<aop:aspect> |
定义一个切面 |
<aop:aspectj-autoproxy> |
启用@AspectJ注解驱动的切面 |
<aop:before> |
定义一个AOP前置通知 |
<aop:config> |
顶层AOP配置元素。大多数的<aop:*> 元素必须包含在次元素内。 |
<aop:declare-parents> |
以透明的方式为被通知的对象引入额外的接口 |
<aop:pointcut> |
定义一个切点 |
定义前置通知&后置通知&环绕通知
定义前置通知: <aop:before pointcut="" method="">
<aop:before pointcut-ref="" method="">
可重用切点:
<aop:pointcut id="" expression="AspectJ切点表达式,可带参数(args(参数名))">
,定义在<aop:aspect>
内,只为当前切面的通知所引用。如果想被多个切面引用,声明在<aop:config>
内。
定义后置通知: <aop:after pointcut="" method="">
<aop:after pointcut-ref="" method="">
定义环绕通知: <aop:around pointcut="" method="">
<aop:around pointcut-ref="" method="">
为什么在xml中需要环绕通知?
当需要在前置通知、后置通知中共享变量的时候,一般来说可以借助一个成员变量来实现。但是,spring中大部分bean是单例的,多线程下是不安全的。环绕通知一般可以完成前置、后置通知的功能,将通知逻辑在一个方法内实现,所以不需要成员变量来保存状态。
为环绕通知传递参数
在引用的或者属性pointcut中,在expression参数里带上“and args(参数名)”即可。
注入AspectJ切面
当spring aop不能满足要求时,如连接点的细粒度,可以使用AspectJ切面。
对于大部分功能来讲,AspectJ切面与Spring是相互独立的。虽然它们可以织入到任意的Java应用中,这也包括了Spring应用,但是在应用AspectJ切面时几乎不会涉及到Spring。但是精心设计且有意义的切面很可能依赖其他类来完成它们的工作。如果在执行通知时,切面依赖于一个或多个类,我们可以在切面内部实例化这些协作的对象。但更好的方式是,我们可以借助Spring的依赖注入把bean装配进AspectJ切面中。
首先必须明确一点,AspectJ切面根本不需要Spring就可以织入应用中。
如果想使用spring的依赖注入为AspectJ切面注入协作者,那就需要在spring配置中把切面声明为一个spring配置中的<bean>
。
注意:spring bean由spring容器初始化,但是AspectJ切面是由AspectJ在运行期创建的。等到Spring为AspectJ切面注入协作者时,AspectJ切面已经被实例化了。所以,我们需要一种方式为Spring获得已经由AspectJ创建的切面实例的句柄,从而可以实现注入。幸好,所有的AspectJ切面都提供了一个静态的aspectOf()方法,该方法返回切面的一个单例。所以为了获得切面的实例,必须在
<bean>
中使用factory-method来调用aspectOf()方法而不是简单调用构造器方法:<bean id="" class="" factory-method="aspectOf">
结语
虽然都是《Spring in Action》一书中关于spring aop的内容,说是梳理,也只是照搬照抄,按照自己理解的内容稍稍整理下,不喜勿喷~也望各位大佬们多多提点~~很愿意学习