AOP配置(含注解)详解

原文

AOP配置详解

解释完了AOP的底层,学习了各个术语,接下来开始应用。<br />这里先写一个需要被增强的类

UserDaoImpl.java

package com.ezeta.dao;

public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("保存操作");
    }

    @Override
    public String   update() {
        System.out.println("更新");
        return "update---res";
    }

    @Override
    public void delete() {
        System.out.println("删除操作");
    }

    @Override
    public void find() {
        System.out.println("find");
    }
}

<a name="jKn1I"></a>

1. 前置步骤

  • step1:引入依赖jar包:

Spring基本jar包(略)、aop相关jar包,(aop-jar包,aspectJ-jar包,aopalliance-jar包)<br />最后一个jar包是aop联盟。

image.png
image.png
  • step2:配置文件中aop约束:

applicationContext.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">
</beans>

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"
这两个就是aop约束

<a name="ffIP8"></a>

2. 编写切面类

要讲封装好的各个通知封装到切面类中。

Myaspect.java

public class Myaspect {
    public void check(){
        System.out.println("权限校验");
    }

    public void log(Object res){
        System.out.println("日志记录"+res);
    }

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("开启事务");
        Object proceed = joinPoint.proceed();
        System.out.println("提交事务");
        return  proceed;
    }

    public void exceptionM(Throwable ex){
        System.out.println("有异常"+ex.getMessage());
    }
    public void after(){
        System.out.println("after通知");
    }
}

以上每个通知都是不同的通知类型,下面会详说。<br />当然,既然是Spring框架的AOP,当然要把切面类也交给SpringIOC管理。(bean的配置略)
<a name="QrG8F"></a>

3.开始配置AOP

<aop:config>
        <!--切点:给哪个方法增强-->
        <aop:pointcut id="savepoint1" expression="execution(* com.ezeta.dao.UserDaoImpl.save(..))"/>
        <aop:pointcut id="savepoint2" expression="execution(* com.ezeta.dao.UserDaoImpl.update(..))"/>
        <aop:pointcut id="savepoint3" expression="execution(* com.ezeta.dao.UserDaoImpl.delete(..))"/>
        <aop:pointcut id="savepoint4" expression="execution(* com.ezeta.dao.UserDaoImpl.find(..))"/>
        <!--配置切面:增强是功能是什么 -->
        <aop:aspect ref="myaspect">
            <!--前置通知-->
            <aop:before method="check" pointcut-ref="savepoint1" />
            <!--后置通知-->
            <aop:after-returning method="log" pointcut-ref="savepoint2" returning="res"/>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="savepoint3"/>
            <!--异常通知-->
            <aop:after-throwing method="exceptionM" pointcut-ref="savepoint4" throwing="ex"/>
            <!--最终通知-->
            <aop:after method="after" pointcut-ref="savepoint4"/>
        </aop:aspect>
</aop:config>

以上每个aop配置也都对应着是不同的通知类型,下面会详说。<br />这里只需先看简单的配置过程:(这里将切入点的类和切面类的bean写上)
<a name="hX8ZS"></a>

<bean id="userDao" class="com.ezeta.dao.UserDaoImpl"></bean>
<bean id="myaspect" class="com.ezeta.aop.Myaspect"></bean>
<aop:config>
    <!--配置切点:给哪个方法增强-->
  <aop:pointcut id="savepoint1" expression="execution(* com.ezeta.dao.UserDaoImpl.save(..))"/>
     <!--配置切面:增强是功能是什么 -->
  <aop:aspect ref="myaspect">
        <!--配置通知-->
        <aop:before method="check" pointcut-ref="savepoint1" />
  </aop:aspect>
</aop:config>

可见,在aop配置标签中,先配置切点,expression语句是execution(* 切点的全路径.方法名(..);<br />然后配置切面,将切面的Springbean的id传进来;<br />在切面配置标签内配置通知,传通method=知方法名、pointcut-ref=切点id;<br />这样,SpringAOP就可以通过全路径找到切点,通过bean找到切面,通过方法名找到切面的通知,以及通过切点id与通知一起进行织入(产生代理)。
<a name="msi1O"></a>

4. 通知的分类

介绍了AOP配置的整个流程,接下来对上述代码通知&配置文件中所用的所有通知类型进行归纳,其中有注释说明可供参考
<a name="1JKqA"></a>

1. 前置通知

基本配置AOP处所举例用到的就是前置通知,在切点执行之前执行,没什么特殊的。
<a name="fOdy7"></a>

2. 后置通知

顾名思义在切点执行之后执行,注意标签不是after是after-returning,既然有“returning”说明这个标签还有特殊的地方。它比普通前置通知多一个属性returning="res",它可以接收切点的返回值,通过通知的参数传进来,参数类型可以直接设Object(见代码),但是要求属性returning的值必须是和通知的参数名字一致。例子中将切点update方法增了个String的返回值“ return "update---res";”,然后由它的通知log(Object res)方法接收。
<a name="8mbBf"></a>

3. 环绕通知

依然顾名思义,是能让通知在切点执行前后都有增强,甚至还能阻止切点运行,这点非常像Filter过滤器.<br />环绕通知的配置与前置一样就不说了,就是标签变成around。<br />环绕的通知方法写法就有些要求:

1. 有个参数**ProceedingJoinPoint joinPoint,**参数就是切点这个方法;
1. 要抛个异常;
1. 如果要让切点执行,需要调用参数joinPoint的方法:**Object proceed = joinPoint.proceed();**并取得返回值proceed,调用这个方法前后就可以写一些环绕它的增强的内容了;4.最后这个通知要返回proceed对象。

<a name="lay20"></a>

4. 异常抛出通知

只有切点抛出异常,才会调用这个通知方法,标签里面多个属性throwing="ex",在通知方法中用参数接收切点抛出的异常(参数类型当然是异常类啦),这里参数名也是和属性throwing的值一致。
<a name="g5ILj"></a>

5. 最终通知

after标签是最终通知才会用,首先是后执行的通知,其次是和上面异常抛出通知比较,最终通知是不论抛没抛异常都执行。
<a name="LuYno"></a>

5. AOP切入点表达式

基于execution函数完成,也就是 属性expression=的值啦。<br />格式:execution ([访问修饰符] 方法返回值 包名.类名.方法名(参数));

  • public com.ezeta.demo2.GoodsDaoImpl.save(..) ,“...”代表参数为任意参数
    • com.ezeta.demo2.GoodsDaoImpl.save(..),“*”代表省略返回值(即任意返回值)
    • com.ezeta.demo2.GoodsDaoImpl+.save(..) “+”代表这个类以及当前类和子类
  • * com.ezeta.*.(..) ,第一个“__”代表com.ezeta下的所有子包和子类;第二个“*”代表类里面的任意方法。

6. AOP注解开发

和IOC&DI一样,Spring喜欢注解开发。<br />AOP注解很简单,其中主要就是对切面类打上各种注解

  • step1:在配置文件中开启aop注解开发(和IOC&DI一样)

当然被代理类,切面类的<bean/>也要写好

<aop:aspectj-autoproxy/>
  • step2:在切面类上添加注解@Aspect(告诉Spring这是切面)

step3:在各个通知上面添加相应注解@注解(value="[execution表达式]")

@Aspect
public class Myaspect {

    @Before(value="execution(* com.ezeta.dao.UserDaoImpl.save(..))")
    public void check(){
        System.out.println("权限校验");
    }

    @AfterReturning(value="execution(* com.ezeta.dao.UserDaoImpl.update(..))",
                   returning="res")
    public void log(Object res){
        System.out.println("日志记录"+res);
    }

    @Around(value="execution(* com.ezeta.dao.UserDaoImpl.delete(..)))
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("开启事务");
        Object proceed = joinPoint.proceed();
        System.out.println("提交事务");
        return  proceed;
    }

    @AfterThrowing(value="execution(* com.ezeta.dao.UserDaoImpl.find(..))",
                  throwing="ex")
    public void exceptionM(Throwable ex){
        System.out.println("有异常"+ex.getMessage());
    }
            
    @After(value="execution(* com.ezeta.dao.UserDaoImpl.find(..))")
    public void after(){
        System.out.println("after通知");
    }
}

可以发现,通知的注解配置其实和标签名字一样,并且就是讲标签中的属性放到注解的参数当中了(只用写execution表达式即可)。其余没有任何变化,比如返回值名字要和通知参数一样等。
前置通知、环绕通知、最终通知一模一样
后置通知注解参数加上returning(逗号隔开各个参数)就接收返回值,也可以不加(不接收),异常通知同理。

还可以配置切入点:<br />在切面类当中,使用注解@Pointcut(value="[execution表达式]"),在下面跟一个空方法,方法名即为切点id

@Aspect
public class Myaspect {

    @Before(value="Myaspect.pointcut1() || Myaspect.pointcut2()")
    public void check(){
        System.out.println("权限校验");
    }

    @Pointcut(alue="execution(* com.ezeta.dao.UserDaoImpl.save(..)) || execution(* com.ezeta.dao.StudentDaoImpl.save(..))")
    private void pointcut1(){}//空方法
    
    @Pointcut(alue="execution(* com.ezeta.dao.UserDaoImpl.save2(..))")
    private void pointcut2(){}
}

可以看出,可以直接在通知注解参数中写此类的切点注解的方法,其中“||”可以连接多个切点,或者多个execution表达式。

没有总结<br />完

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

推荐阅读更多精彩内容