AOP通知类型与XML配置

AOP通知类型与XML配置

Timber 2,754 2021-06-08

AOP通知类型与XML配置

AOP的通知类型共5种

  • 前置通知:原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行
    应用:数据校验
  • 后置通知:原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知
    应用:现场清理
  • 返回后通知:原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行
    应用:返回值相关数据处理
  • 抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
    应用:对原始方法中出现的异常信息进行处理
  • 环绕通知:在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行
    应用:十分强大,可以做任何事情

AOPAdvice通知类

package com.timber.aop;

import org.aspectj.lang.ProceedingJoinPoint;

//1.制作通知类,在类中定义一个方法用于完成共性功能
public class AOPAdvice{
    public void function(){
        System.out.println("共性功能...");
    }

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

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

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

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

    public void around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("around ... before");
        //对原始方法的调用
        pjp.proceed();
        System.out.println("around ... after");
    }
}

前置通知

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *(..))"/>
    <aop:aspect ref="myAdvice">
        <aop:before method="before" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>

后置通知

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *(..))"/>
    <aop:aspect ref="myAdvice">
        <aop:after method="after" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>

返回后通知

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *(..))"/>
    <aop:aspect ref="myAdvice">
        <aop:after-returning method="afterReturing" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>

抛出异常后不执行

抛出异常后通知

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *(..))"/>
    <aop:aspect ref="myAdvice">
        <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>

环绕通知

<aop:config>
    <aop:pointcut id="pt" expression="execution(* *(..))"/>
    <aop:aspect ref="myAdvice">
        <aop:around method="around" pointcut-ref="pt"/>
    </aop:aspect>
</aop:config>

环绕通知是在原始方法的前后添加功能,在环绕通知中,存在对原始方法的显示调用

public Object around(ProceedingJoinPoint pjp) throws Throwable{
    System.out.println("around ... before");
    //对原始方法的调用
    Object ret = pjp.proceed();
    System.out.println("around ... after");
    return ret;
}
  • 方法须设定 Object类型的返回值,否则会拦截原始方法的返回。如果原始方法返回值类型为
    void,通知方法也可以设定返回值类型为void,最终返回null。
  • 方法需在**第一个参数位置设定ProceedingJoinPoint对象**,通过该对象调用 proceed()方法,实
    现对原始方法的调用。如省略该参数,原始方法将无法执行。
  • 使用 proceed()方法调用原始方法时,因无法预知原始方法运行过程中是否会出现异常,强制抛
    出 Throwable对象,封装原始方法中可能出现的异常信息。

基于XML配置的通知顺序

当同一个切入点配置了多个通知时,通知会存在运行的先后顺序,该顺序以通知配置的顺序为准。

<aop:before method="before3" pointcut-ref="pt"/>
<aop:before method="before" pointcut-ref="pt"/>
<aop:before method="before2" pointcut-ref="pt"/>

通知获取参数数据

1.设定通知方法第一个参数为JoinPoint,通过该对象调用getArgs()方法,获取原始方法的参数数组

public void before(JoinPoint jp){
    Object[] args = jp.getArgs();
    System.out.println("before ..." + args[0]);
}

2.设定切入点表达式为通知方法传递参数(改变通知变量名的定义顺序)

原始方法

public class UserServiceImpl implements UserService{
    public void save(int i, int j){
        System.out.println("user service running ..." + i + "," + j);
    }
}

XML配置

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* *(..))"/>
        <aop:aspect ref="myAdvice">
           <aop:after method="after"
                       arg-names="y,x"
                       pointcut="execution(* *(int,int)) &amp;&amp; args(x,y) "/>
        </aop:aspect>
    </aop:config>

arg-names="y,x" 改变参数传入通知的顺序

通知类

public void after(int x , int y){
    System.out.println("after ..."+ x+","+y);
}

#### 输出

通知获取返回值数据

1.适用于返回后通知( after-returning )

原始方法

public int update(){
    System.out.println("user service update ...");
    return 200;
}

#### XML配置

returning="ret" 配置属性变量名

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* *(..))"/>
        <aop:aspect ref="myAdvice">
			<aop:after-returning method="afterReturing"-->
                                pointcut-ref="pt" returning="ret"/>
        </aop:aspect>
    </aop:config>

#### 通知类

public void afterReturing(Object ret){
    System.out.println("afterReturing ..." + ret);
}

输出

2.适用于环绕通知(around)

原始方法

public int update(){
    System.out.println("user service update ...");
    return 200;
}

XML配置

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* *(..))"/>
        <aop:aspect ref="myAdvice">
            <aop:around method="around" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>

通知类

public Object around(ProceedingJoinPoint pjp) throws Throwable{
    System.out.println("around ... before");
    //对原始方法的调用
    Object ret = pjp.proceed();
    System.out.println("around ... after" + ret);
    return ret;
}

输出

通知获取异常数据

1.适用于返回后通知(after-throwing)

设定异常对象变量名

原始方法

public void delete(){
    System.out.println("user service delete ...");
    int i = 1/0; //抛异常
}

XML配置

        <aop:config>
            <aop:pointcut id="pt" expression="execution(* *(..))"/>
            <aop:aspect ref="myAdvice">
                <aop:after-throwing method="afterThrowing"
                                    pointcut-ref="pt" throwing="t"/>
            </aop:aspect>
        </aop:config>

throwing="t" 设定异常对象名传入通知类

通知类

public void afterThrowing(Throwable t){
    System.out.println("afterThrowing ..." + t.getMessage());
}

输出

2.适用于环绕通知(around)

在通知类中的方法中调用原始方法捕获异常

#### 原始方法

public void delete(){
    System.out.println("user service delete ...");
    int i = 1/0; //抛异常
}

XML配置

        <aop:config>
            <aop:pointcut id="pt" expression="execution(* *(..))"/>
            <aop:aspect ref="myAdvice">
                <aop:around method="around" pointcut-ref="pt"/>
            </aop:aspect>
        </aop:config>

通知类

public Object around(ProceedingJoinPoint pjp){
    System.out.println("around ... before");
    //对原始方法捕获异常并且处理
    Object ret = null;
    try {
        ret = pjp.proceed();
    } catch (Throwable throwable) {
        System.out.println("around... exception..." + throwable.getMessage());
    }
    System.out.println("around ... after" + ret);
    return ret;
}

输出