Spring AOP

2017-08-28  本文已影响0人  Q南南南Q

一、增强类

1 前置增强(MethodBeforeAdvice)
重写before(Method method,Object[] args,Object obj)方法,method为目标类的方法;args为目标类方法参数;obj为目标类实例。

2 后置增强(AfterReturningAdvice)
重写afterReturning(Object returnObj,Method method,Object[] args,Object obj)方法,returnObj为目标方法返回的结果,其余参数合和befor方法一致。

3 环绕增强(MethodInterceptor)
重写invoke(MethodInvocation)方法,MethodInvocation不仅封装了目标方法及其入参数组,还封装了目标方法所在的实力对象,通过MethodInvocation的getArguments方法可以获取目标方法的入参参数,通过proceed方法反射调用目标实例相应的方法。

4 异常抛出增强(ThrowsAdvice)
重写afterThrowing(Method method,Object[] args,Object target,Exception ex)方法,方法参数规定如下:前三个参数(method,args,target)是可选的(要么三个全部提供,要么都不不提供),最后一个参数是Throwable或其子类。

5 引介增强(IntroductionInterceptor)
该接口没有定义任何方法,Spring为该接口提供了DelegatingIntroductionInterceptor实现类,一般情况下通过继承该类,覆盖invoke(MethodInvocation mi)方法来定义自己的引介增强类。

二、切面

介绍增强时,我们注意到增强被织入到目标类的所有方法中,如果我们要有选择地进行织入,就需要使用切点进行目标连接点的定位了。

增强提供了连接点的方位信息:如织入到方法前、方法后,而切点进一步描述织入到哪些类的哪些方法上。

Spring通过PointCut接口描述切点,PointCut由ClassFilter和MethodMatcher构成,它通过ClassFilter定位到类,通过MethodMatcher定位到方法,这样PointCut就拥有了描述某些类的某些具体方法的能力。

PiontCut类关系图

ClassFilter只定义了matches(Class clazz),其参数代表一个被检测的类,该方法判别被检测的类时候匹配过滤条件。

MethodMatcher支持静态方法匹配以及动态方法匹配。静态方法匹配仅对方法名进行匹配,且只会判别一次;动态方法匹配会在运行时检查方法入参的值,所以每次调用方法都会进行判断。

切点类型

Spring提供了6中切点:

切面类型

增强包含横切代码,又包含部分连接点信息,所以我们可以通过增强类生成一个切面。切点只包含类和方法信息,所以要结合增强才能制作出切面。

三、织入切面到目标类的方法

1 通过ProxyFactory代理工厂

Spring中通过使用ProxyFactory将增强织入到目标类中,ProxyFactory内部就是使用JDK代理或CGLib代理技术,将增强织入到目标类中。

Spring定义了AopProxy接口,并提供了两个final类型的实现类:

AopProxy类结构

2 通过Spring配置
在配置文件中定义如下Bean:

<bean id="objName" class="org.springframework.aop.framework.ProxyFactoryBean" 
    p:proxyInterfaces="代理接口,如果是多个接口,使用list元素"
    p:interceptorNames="指定使用的增强"
    p:target-ref="指定对那个Bean进行代理"
    p:proxyTargetClass="是否对类进行代理,设置为true,使用CGLib代理" />

3 自动创建代理

Spring提供了自动创建代理机制,让容器为我们自动生成代理。在内部,Spring使用BeanPostProcessor自动完成这项工作。

基于BeanPostProcessor的自动代理创建器的实现类,将根据一些规则在容器实例化Bean时为匹配的Bean生成代理实例。代理创建器可以分为以下三类:

四、基于注解的AOP

在基于注解的方式中,我们利用@AspectJ来描述切点、增强,和之前的PointCut和Advice相比,两者只是表述方式不同。
下面是一个例子:

我们惊奇的发现,这个切面只是一个普通的POJO,特殊的地方在于标注了@AspectJ注解。

如何通过配置使用@AspectJ切面

在上一节中我们介绍的自动代理创建器,其中AnnotationAwareAspectJAutoProxyCreator可以将@AspectJ注解的切面织入到目标Bean中。

如果使用基于Schema的aop命名空间进行配置,那就更加简单了:

首先在配置文件中引入aop的命名空间,如①、②出所示,然后通过aop命名空间的

<aop:aspectj-autoproxy>

自动为Spring容器中那些匹配@AspectJ切面的Bean创建代理,完成切面织入(Spring内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行代理的创建工作)。

@AspectJ语法基础

1 增强类型

@Before
前置增强,相当于BeforeAdvice,有两个成员:

@AfterReturning
后置增强,相当于AfterReturningAdvice,有四个成员:

@Around
环绕增强,相当于MethodInterceptor,有两个成员:

@AfterThrowing
环绕增强,相当于ThrowsAdvice,有四个成员:

@After
final增强,不管是异常抛出还是正常退出,该增强都会执行,有两个成员:

@DeclareParents
引介增强,相当于IntroductionInterceptor,有两个成员:

2 切点函数表达式
Spring支持9个@AspectJ切点表达式函数,大致分为四种类型:

四种类型的切点函数如下进行说明:

@annotation()

@annotation表示标注了某个注解的所有方法。例如:

假设类NaughtyWaiter#greetTo()方法标注了@NeedTest注解,那么该方法将会被织入增强。

execution()

是最常用的切点函数,语法如下:

execution(<修饰符模式>?<返回类型模式><方法模式>(<参数模式>)<异常模式>?)

修饰符模式和异常模式是可选的。

args()和@args()

args()接受一个类名,表示目标类方法入参对象是指定类时(包含子类),切点匹配。例如:

args(com.lzn.Waiter),等价于execution(* *(com.lzn.Waiter+))

表示运行时入参是Waiter类型的方法,则匹配。

@args()接受一个注解类的类名,当方法的运行时入参对象标注了指定的注解时,方法匹配切点。

@args(M)匹配示意图

within()

within()定义的连接点是针对目标类而言的。语法如下所示:

within(<类匹配模式>)

各种切面类型总结

切点不同定义方式总结

LTW(Load Time Weaving)

AOP切面织入除了通过JDK动态代理以及CGLib代理的方式实现之外,还可以通过在类加载期通过字节码编辑技术将切面植入到目标类中,这种方式叫做LTW。

Spring的LTW仅支持@AspectJ定义的切面,它利用类路径下的META_INF/aop.xml配置文件找到切面定义以及切面所要实施的候选目标类的信息,通过LoadTimeWeaver在ClassLoader加载类文件时,将切面织入到目标类中,工作原理如图所示:

LTW工作原理

Spring利用特定web容器的ClassLoader,通过LoadTimeWeaver将Spring提供的ClassFileTransformer注册到ClassLoader中。在类加载时期,注册的ClassFileTransformer读取META_INF/aop.xml配置文件,获取切面,对加载到VM中的Bean类进行字节码转换,织入切面。Spring容器初始化Bean实例时,采用的Bean类就是已经织入切面的类。

Spring的LoadTimeWeaver

大多数web应用服务器(Tomcat除外)的ClassLoader都支持直接访问Instrument,无需通过javaagent参数指定代理,拥有这种能力的ClassLoader称为“组件使能”。通过“组件使能”功能,可以非常方便的访问到ClassLoader的Instrument。Spring利用了web应用服务器类加载的这个特性,为他们提供了专门的LoadTimeWeaver。以便向特定的ClassLoader注册ClassFileTransformer,对类进行字节码转换,实现切面织入。

LoadTimeWeaver接口有三个方法:

Spring只需在配置文件中加入一行配置就能启用LoadTimeWeaver

<context:load-time-weaver>

使用LTW织入切面实例

第一步:定义切面

第二步:创建可被增强类

第三步:Spring配置文件

<context:load-time-weaver>
<bean class="com.baobaotao.ltw.Waiter">

第四步:在src下创建META-INF目录,并在该目录下新增AspectJ的配置文件aop.xml:

<aspectj>
    <aspects>
        // 切面
        <aspect name="com.baobaotao.ltw.PreGreetingAspect" />
    </aspects>
    <weaver options="-showWeaveInfo 
        -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMeaasgeHandler">
        // 要织入增强的目标类
        <include within="com.baobaotao.ltw.*" />
    </aspect>
</aspectj>
上一篇 下一篇

猜你喜欢

热点阅读