spring中的AOP(1)

297 阅读3分钟

spring中的AOP

AOP的XML配置

导入坐标

  • 在pom中导入==aspectjweaver==的坐标
<dependency>
	<groupId>org.aspectweaver</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>

在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">

配置步骤

  1. 把通知的bean也交给spring来管理

  2. 使用aop:config标签表明开始aop的配置

  3. 使用aop:aspect标签开始配置切面

    ​ id属性:是给切面提供的唯一标识

    ​ ref属性:是指定通知类bean的id

  4. aop:aspect标签内部使用对应的标签来配置通知的类型

    ​ aop:before:标识配置的前置通知

    ​ method属性:用于指定Logger类中哪个方法是前置通知

    ​ pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

    ​ 切入点表达式的写法:

    ​ 关键字:execution(表达式)

    ​ 表达式:

    ​ 访问修饰符 返回值 包名.包名...类名.方法名(参数列表)

    ​ 标准的表达式写法:

    ​ public void com.itcast.service.imp.AccountServiceImpl.saveAccount()

    ​ 访问修饰符可以省略 其余的可以使用通配符*

    ​ 参数列表可以直接写数据类型:

    ​ 基本数据类型直接写名称:int

    ​ 引用类型包名.类名:java.lang.String

    ​ 可以是同通配符*表示任意类型,但是必须有参数

    ​ 可以使用..表示有误参数均可,任意类型都可

    ​ 全通配写法:

    * *..*.*(..)
    

    ​ 实际开发中的常用写法:

    ​ 切到业务层实现类下的所有方法

    ​ * com.itcast.service.imp. * . *(..)


<!--被切的bean-->
<bean id="accountService" class="com.itcast.service.imp.AccountServiceImp"></bean>

<!--切的方法所在的bean-->
<bean id="logger" class="com.itcast.utils.Logger"></bean>

<!--配置切面-->
<aop:config>
	<aop:aspect id="aspect1" ref="logger"><!--在切面中引入切入方法所在的bean-->
    	<aop:before method="printLog" pointcut="public void com.*.*.*.saveAccount()"></aop:before>
    </aop:aspect>
</aop:config>

通用的切入点表达式

方式一

  • 将切入点表达式写在aop切面标签的内部
<aop:config>
	<aop:aspect id="as1" ref="logger">
        <!--配置通用的切入点表达式-->
    	<aop:pointcut id="pt1" expression="execution(* com.itcast.service.imp.*.*(..))"/>
        <!--引用切入点表达式-->
        <aop:before method="PringLog" pointcut-ref="pt1"></aop:before>
    </aop:aspect>
</aop:config>
  • ==注意==:当切入点表达式写在切面内部,只有该切面能够引用此切入点表达式

方式二

  • 将切入点表达式写在aop切面标签的外部
<aop:config>
    <!--配置通用的切入点表达式,只能写在所有切面的前面(xml的约束)-->
    <aop:pointcut id="pt1" expression="execution(* com.itcast.service.imp.*.*(..))"/>
    <!--配置切面-->
	<aop:aspect id="as1" ref="logger">
        <!--引用切入点表达式-->
        <aop:before method="PringLog" pointcut-ref="pt1"></aop:before>
    </aop:aspect>
</aop:config>

四种常用的通知

前置通知

  • 标签:<aop:before>
  • 作用时机:在切入点方法执行前执行
  • 在环绕通知中的位置:在切入点方法执行前的代码

后置通知

  • 标签:<aop:after-returning>
  • 作用时机:在切入点方法执行之后,并且没有异常时执行
  • 在环绕通知中的位置:在切入点方法执行后的代码
  • ==注意==:后置通知和异常通知永远只能执行一个

异常通知

  • 标签:<aop:after-throwing>
  • 作用时机:在切入点方法执行之后,出现了异常时执行
  • 在环绕通知中的位置:在catch中的代码

最终通知

  • 标签:<aop:after>
  • 作用时机:无论切入点方法是否正常执行,都会在最后执行
  • 在环绕通知中的位置:在finally中的代码

环绕通知

  • 注意:在环绕通知中要手动调用切入点的方法,否则只会执行通知中的方法

环绕通知的配置方式

  • 和前置、后置通知一样,在<aop:aspect>标签中配置
<aop:config>
    <!--配置切入点表达式-->
    <aop:pointcut id="pt1" expression="execution(* com.itcast.service.imp.*.*(..))"></aop:pointcut>
	<aop:aspect id="xx1" ref="logger">
    	<!--配置环绕通知-->
        <aop:arround method="xxx" pointcut-ref="pt1"></aop:arround>
    </aop:aspect>
</aop:config>

环绕通知的方法

//通知类
public class Logger{
    //环绕通知
    public Object arrountPrintLog(ProceedingJoinPoint pjp){
        //返回值
        Object rtValue=null;
        try{
            //获取参数列表
            Object[] args = pjp.getArgs();
            //前置通知
            rtValue = pjp.proceed(args);//执行切入点方法
            //后置通知
        }catch(Throwable t){
            //异常通知
            throw new RuntimeException(t);
        }finally{
            //最终通知
        }
        return reValue;
    }
}
  • ==ProceedingJoinPoint==对象

    ​ 用于在环绕通知中,获取参数列表,执行切入点方法