【Spring入门系列】篇6:再探SpringAOP(SpringAOP的使用)

前言

前面一篇文章中我们简单介绍了SpringAOP的作用以及原理,接下来我们来讲讲SpringAOP怎么配置使用。

一、SpringAOP的基本使用

还是使用上篇中的例子,假设我们需要给GoodsDao类进行增强:

public class GoodsDao {
    public void save() {
        System.out.println("goodsDao-----save");
    }
}

1.编写切面类

切面类中主要包含了多个通知(增强功能所封装成的方法)

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

2.配置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">
    <bean id="goodsDao" class="demo1.GoodsDao"></bean>
    <bean id="myAspect" class="demo2.MyAspectXML"></bean>

    <!--配置Aop-->
    <aop:config>
        <!--配置切点:需要增强的方法-->
        <aop:pointcut id="savePoint" expression="execution(* demo1.GoodsDao.save(..))"/>
        <!--配置切面:如何增强、增强的功能-->
        <aop:aspect ref="myAspect">
            <aop:before method="check" pointcut-ref="savePoint"/>
        </aop:aspect>

    </aop:config>

</beans>

第一步:配置applicationContext肯定是将需要增强的类、切面类交由Spring管理

    <bean id="goodsDao" class="demo1.GoodsDao"></bean>
    <bean id="myAspect" class="demo2.MyAspectXML"></bean>

第二步:在aop配置配置切点,我们当然要指定需要被增强的方法(切点)。expression表达式指定需要配置的方法,*表示任意返回值..表示任意参数。

<!--配置切点:需要增强的方法-->
<aop:pointcut id="savePoint" expression="execution(* demo1.GoodsDao.save(..))"/>

第三步:配置切面,配置完切点我们还需要配置切面来通知Spring需要增强的内容是什么,以及如何进行增强

<!--配置切面:如何增强、增强的功能-->
<aop:aspect ref="myAspect">
<aop:before method="check" pointcut-ref="savePoint"/>
</aop:aspect>

3.测试

import demo1.GoodsDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AspectTest {

    @Resource(name = "goodsDao")
    GoodsDao goodsDao;

    @Test
    public void test() {
        goodsDao.save();
    }
}

运行结果:

增加权限校验
goodsDao-----save

二、SpringAOP通知类型

在动态代理中我们提到过我们可以在方法前面进行增强,也可以在方法后进行增强,springAOP也是如此,给我们提供了下面几种方式:

  • 前置通知(before):在方法执行前通知
  • 后置通知(after-returning):在方法执行后通知
  • 环绕通知(around):在方法执行前后通知
  • 异常抛出通知(after-throwing):在方法发生异常时通知
  • 最终通知(after):无论是否发生异常都通知

我们先将GoodsDao修改一下:

public class GoodsDao {
    public void save() {
        System.out.println("goodsDao-----save");
    }

    public String delete() {
        System.out.println("delete");
        return "delete---return";
    }

    public void update() {
        System.out.println("update");
    }

    public void find() {
        System.out.println("find");
        int a = 1/0;
    }

}

接着修改一下切面类:

import org.aspectj.lang.ProceedingJoinPoint;

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

    public void writeLog(Object res) {
        System.out.println(res);
        System.out.println("记入日志");
    }

    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around------前置通知");
        joinPoint.proceed();
        System.out.println("around------后置通知");
    }

    public void exceptionNotice(Throwable e) {
        System.out.println("发生了异常:"+ e.getMessage());
    }

    public void finalNotice() {
        System.out.println("final--------------");
    }

}

配置applicationContex.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 id="goodsDao" class="demo1.GoodsDao"></bean>
    <bean id="myAspect" class="demo2.MyAspectXML"></bean>

    <!--配置Aop-->
    <aop:config>
        <!--配置切点:需要增强的方法-->
        <aop:pointcut id="savePoint" expression="execution(* demo1.GoodsDao.save(..))"/>
        <aop:pointcut id="deletePoint" expression="execution(* demo1.GoodsDao.delete(..))"/>
        <aop:pointcut id="updatePoint" expression="execution(* demo1.GoodsDao.update(..))"/>
        <aop:pointcut id="findPoint" expression="execution(* demo1.GoodsDao.find(..))"/>
        <!--配置切面:如何增强、增强的功能-->
        <aop:aspect ref="myAspect">
            <!--前置通知-->
            <aop:before method="check" pointcut-ref="savePoint"/>
            <!--后置通知-->
            <aop:after-returning method="writeLog" returning="res" pointcut-ref="deletePoint"/>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="updatePoint"/>
            <!--异常抛出通知-->
            <aop:after-throwing method="exceptionNotice" throwing="e" pointcut-ref="findPoint"/>
            <!--最终通知-->
            <aop:after method="finalNotice" pointcut-ref="findPoint"/>

        </aop:aspect>

    </aop:config>

</beans>

测试类:

import demo1.GoodsDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AspectTest {

    @Resource(name = "goodsDao")
    GoodsDao goodsDao;

    @Test
    public void test() {
        goodsDao.save();
        System.out.println("--------------------------------------------");
        goodsDao.delete();
        System.out.println("--------------------------------------------");
        goodsDao.update();
        System.out.println("--------------------------------------------");
        goodsDao.find();
    }
}

运行结果:

增加权限校验
goodsDao-----save
--------------------------------------------
delete
delete---return
记入日志
--------------------------------------------
around------前置通知
update
around------后置通知
--------------------------------------------
find
final--------------
发生了异常:/ by zero

三、SpringAOP注解编程

我们可以将上面的程序改为注解的形式:

修改applicationContex.xml,需要在配置中开启aop注解:

<?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 id="goodsDao" class="demo1.GoodsDao"></bean>
    <bean id="myAspect" class="demo2.MyAspectXML"></bean>

    <!--AOP开启注解-->
    <aop:aspectj-autoproxy/>
</beans>

修改切面类,用法与配置文件形式基本相同:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class MyAspectXML {

    @Before(value = "execution(* demo1.GoodsDao.save(..))")
    public void check() {
        System.out.println("增加权限校验");
    }

    @AfterReturning(value = "execution(* demo1.GoodsDao.delete(..))" , returning = "res")
    public void writeLog(Object res) {
        System.out.println(res);
        System.out.println("记入日志");
    }

    @Around(value = "execution(* demo1.GoodsDao.update(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around------前置通知");
        Object proceed = joinPoint.proceed();
        System.out.println("around------后置通知");
        return proceed;
    }

    @AfterThrowing(value = "execution(* demo1.GoodsDao.find(..))" , throwing = "e")
    public void exceptionNotice(Throwable e) {
        System.out.println("发生了异常:"+ e.getMessage());
    }

    @After(value = "execution(* demo1.GoodsDao.find(..))")
    public void finalNotice() {
        System.out.println("final--------------");
    }

}
注解切入点的配置

有时候我们想在一个切点中即添加前置通知,又添加异常通知和最终通知该如何操作呢?

我们可以在切面当中定义好切入点:

import org.aspectj.lang.annotation.*;

@Aspect
public class MyAspectXML2 {
    @Before(value = "MyAspectXML2.pointcut1()")
    public void check() {
        System.out.println("增加权限校验");
    }

    @AfterThrowing(value = "MyAspectXML2.pointcut1()" , throwing = "e")
    public void exceptionNotice(Throwable e) {
        System.out.println("发生了异常:"+ e.getMessage());
    }

    @After(value = "MyAspectXML2.pointcut1()")
    public void finalNotice() {
        System.out.println("final--------------");
    }

    @Pointcut(value = "execution(* demo1.GoodsDao.save(..))")
    public void pointcut1(){}//定义好切点
}
posted @ 2020-07-01 15:48  ThinMoon  阅读(116)  评论(0编辑  收藏  举报