Spring学习手册(10)—— Spring AOP配置

Spring学习手册(9)—— Spring AOP入门讲述了AOP技术以及AOP基本概念,最后我们了解了Spring对AOP的支持。本文我们将以XML配置的方式来学习Spring AOP的具体使用。

一、引入aop模式

如果想使用XML的方式配置AOP信息,我们需要先在XML配置文件中引入aop模式(aop schema),因此我们的XML配置文件头头信息如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> 
        <!-- bean definitions here -->
</beans>

这样配之后,我们就可以在xml文件里面直接引用aop标签了。

二、定义一个切面

一个Spring AOP的切面(Aspect)在xml中也是一个传统的bean,而使用标签<aop:aspect>定义一个切面,并且使用ref来执行被定义为该切面的bean。

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
    ...
</bean>

如上,我们定义了一个id为aBean的bean实例,然后使用<aop:aspect>标签定义了一个id为myAspect的切面,而该切面指向aBean。

三、定义一个切点(pointcut)

上篇我们说过,切点表达式用于匹配连接点(join point),然后根据配置的增强(Advice)方法在连接点运行时选择合适时间执行。因为Spring AOP目前仅支持运行方法类型的连接点,所以也可以认为与一个切点匹配一个bean的执行方法。

一个切点包含两部分:

  • 包含方法和名字的签名;
  • 切点表达式:用于匹配具体的方法

因此我们使用xml定义切点如下所示:

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..))"/>
        ...
    </aop:aspect>
</aop:config>

<aop:pointcut>必须在<aop:aspect>内部使用定义切点,使用expression的值指明切点表达式,id为该切点定义类唯一标示方便配置引用。这里并没有定义切点(pointcut) 签名,切点签名定义一般在使用@AspectJ注解方式定义切点时定义。下面我们着重说明下切点表达式的语法情况。

支持的切点(pointcut)标示

Spring AOP 支持以下AspectJ 的切点标示(AspectJ pointcut designators)简称PCD,由于Spring AOP并没有全部支持所有的PCD,因此若使用了不存在该列表内的标示则会抛出异常。

标签名 说明
execution 匹配运行方法的连接点
within 使匹配连接点限定在特定类型
this 限定匹配的连接点是给定类型的实例
target 限定匹配连接点的目标是给定类型的实例
args 限定连接点的参数是给定类型的实例
@target 限定匹配的连接点的运行的对象有该类型的注解
@args 运行时传递的参数拥有给定类型的注解
@within 限定匹配给定指定注解类型的连接点
@annotation 限定连接点拥有指定的注解
bean 使得连接点匹配特质的bean或bean集合

Tip:我们可以使用&&||来将连接点表达式关联起来。
然而在XML配置中该类字符需要转换,因此我们可以使用更语义化的句子符号andornot

例子

使用execution例子

 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
        throws-pattern?)

以上除ret-type-patternname-patternparam-pattern为必须外,其他皆为可选。

  • 匹配所有的公有方法
    execution(public * *(..))
  • 匹配所有以set开头的方法
    execution(* set*(..))
  • 匹配所有AccountService接口定义的方法
    execution(* com.xyz.service.AccountService.*(..))
  • 匹配所有service包内的方法
    execution(* com.xyz.service..(..))
  • 匹配所有service包以及子包内的方法
    execution(* com.xyz.service...(..))
  • 所有在service包内的连接点
    within(com.xyz.service.*)
  • 所有service包以及子包内的连接点
    within(com.xyz.service..*)
  • 所有实现AccountService接口的连接点
    this(com.xyz.service.AccountService)
  • 所有的目标对象实现AccountService接口的连接点
    target(com.xyz.service.AccountService)
  • 有一个参数并且运行时传入参数为Serializable的连接点
    args(java.io.Serializable)
  • 目标对象有@Transactional注解
    @target(org.springframework.transaction.annotation.Transactional)
  • 定义的目标对象类型含有@Transactional注解
    @within(org.springframework.transaction.annotation.Transactional)
  • 含有@Transactional注解的可运行方法
    @annotation(org.springframework.transaction.annotation.Transactional)
  • 含有一个参数并且在运行时传入的参数含有@Classified注解
    @args(com.xyz.security.Classified)
  • bean名字定义为tradeService的连接点
    bean(tradeService)
  • bean名字满足Service命名的所有连接点
    bean(
    Service)

四、定义增强方法(Advice)

Before advice
<aop:aspect id="beforeExample" ref="aBean">
    <aop:pointcut id="dataAccessOperation"
            expression="execution(* com.xyz.myapp.dao.*.*(..))"/>
    
    <aop:before
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>

    ...

</aop:aspect>

<aop:before>使用pointcut-ref引用一个切点,并且指定增强执行方法为doAccessCheck。值的注意的是名为aBean的bean(也就是我们定义的切面)必须实现doAccessCheck方法。该方法会在目标方法执行前执行。

After returning advice
<aop:aspect id="afterReturningExample" ref="aBean">
    <aop:pointcut id="dataAccessOperation"
            expression="execution(* com.xyz.myapp.dao.*.*(..))"/>
    <aop:after-returning
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>

    ...
</aop:aspect>

同上,该方法会在切点执行正常返回后执行。
当然如果你需要获取返回对象的话,你需要将配置信息改为如下所示:

<aop:aspect id="afterReturningExample" ref="aBean">
    ...
    <aop:after-returning
        pointcut-ref="dataAccessOperation"
        returning="retVal"
        method="doAccessCheck"/>

    ...

</aop:aspect>

其中doAccessCheck方法定义如下,其中参数名必须与XML里面配置相同,也就是说必须与XML中的retVal一致。

public void doAccessCheck(Object retVal) {...}
After throwing advice
<aop:aspect id="afterThrowingExample" ref="aBean">
    ...
    <aop:after-throwing
        pointcut-ref="dataAccessOperation"
        method="doRecoveryActions"/>

    ...

</aop:aspect>

当然如果需要获取抛出的异常时我们可以如下配置:

<aop:aspect id="afterThrowingExample" ref="aBean">

    <aop:after-throwing
        pointcut-ref="dataAccessOperation"
        throwing="dataAccessEx"
        method="doRecoveryActions"/>

    ...

</aop:aspect>

其中doRecoveryActions方法定义如下且必须有名为dataAccessEx参数:

public void doRecoveryActions(DataAccessException dataAccessEx) {...}
After (finally) advice

切点返回后执行如下增强方法(无论正常返回还是异常退出):

<aop:aspect id="afterFinallyExample" ref="aBean">

    <aop:after
        pointcut-ref="dataAccessOperation"
        method="doReleaseLock"/>

    ...

</aop:aspect>
Around advice 环绕增强方法

该类型的Advice环绕着连接点,该增强方法可以选择调用或者不掉用连接点方法。

<aop:aspect id="aroundExample" ref="aBean">

    <aop:around
        pointcut-ref="businessService"
        method="doBasicProfiling"/>

    ...

</aop:aspect>
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
}

如上所示,我们使用aop:around来定义环绕增强,方法定义的第一个参数必须为ProceedingJoinPoint类,在Around增强方法中,需要调用processed方法才会执行真正的切点方法,否则则会放弃执行切点方法。

Advice 参数

有时我们需要获取到目标方法的参数信息,而Spring AOP为我们提供了方便的获取方式,接下来我们就来了解下如何通过XML配置来获取目标方法的参数信息。
我们定义如下代码

package com.aop.learn.service;

public interface StudentQueryService {

    /**
     * 根据姓名和年龄查询学生信息,
     * 假设无重复现象
    */
    Student  queryStdent(String name,int age);
    
    //...
}
package com.aop.learn.service.impl;
public class StudentQueryServiceImpl implements StudentQueryService {
    public Student queryStdent(String name, int age) {

        // do something 
        // query database or create a new object 
    }
}

切面例子(拦截器)

package com.aop.learn.interceptor;
public class InterceptorSample {

    public Object interceptorMethod(ProceedingJoinPoint call, String name, int age) throws Throwable {
          // do something
          Object result =  call.proceed();
        //do something
    }
}

配置信息如下

<!--定义bean信息-->
<bean id="aspectSample",class="com.aop.learn.interceptor.InterceptorSample">
<!--配置切面信息-->
<aop:config>
        <aop:aspect ref="aspectSample">

            <aop:pointcut id="pointsample"
                expression="execution(* com.aop.learn.service.StudentQueryService.queryStdent(..))
                and args(name, age)"/>
            <aop:around pointcut-ref="pointsample"
                method="interceptorMethod"/>
        </aop:aspect>
    </aop:config>

如上配置信息,在expression语句中增加了args(name,age),该表达式定义了变量名为name和age的参数,该参数名必须和增强方法中名字一致,这样我们就能在增强方法中获取目标方法的参数信息了。

五、总结

本文我们主要学习了如何使用XML配置方式来完成Spring AOP的使用。文章中我们学习了很多配置标签,完成了切面定义、切点定义以及多种增强方法的定义。至此我们已经掌握了Spring AOP的基本知识点,可在具体的项目开发中使用。不过学了那么多的知识点,我们还是需要一定的实践来进行消化吸收。接下来我们将构造例子用来巩固学习到的知识点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容