spring aop aspect相关顺序及相关通知使用

一、好言

有时候,上天没有给你要的,不是因为你不配,而是因为你指的更好的。


二、背景

最近看看《spring揭秘》这本书,也顺带看看源码,第一遍希望能熟悉基本的用法和理解一些简单的源码。


三、内容

3.1、aspect顺序

对于多个Advice 来说,如果它们引用的Pointcut定义恰好匹配同一个Jointpoint的时候,在这同一个Jointpoint 的时候,在这同一个Jointpoint上,这些Advice改按照什么顺序执行了?对于这个问题,我们从两个角度来看

当这些Advice都声明在同一个Aspect内的时候,如果匹配同一个Jointpoint的多个Advice都声明在同一个Aspect定义中,那么这些Advice的执行顺序,由他们在Aspect中的声明顺序决定。最先声明的Advice拥有最高的优先级。对于Before Advice来说,拥有最高优先级的最先运行;而对于AfterReturningAdvice,拥有最高优先级的则最后运行。

eg:

/**
 * @Title: MultiAdvicesAspect
 * @Package org.mouse.spring.aspect
 * @Description: 测试所有的切面advice
 * @author Mahone Wu
 * @date 2017/12/12 15:42
 * @version V1.0
 */
@Component
@Aspect
public class MultiAdvicesAspect {

    @Pointcut("execution(boolean *.execute(String,..))")
    public void taskExecution(){}

    @Before("taskExecution()")
    public void beforeOne(){
        System.out.println("before one");
    }

    @Before("taskExecution()")
    public void beforeTwo(){
        System.out.println("before two");
    }

    @AfterReturning("taskExecution()")
    public void afterReturningOne(){
        System.out.println("after returing one");
    }

    @AfterReturning("taskExecution()")
    public void afterReturningTwo()
    {
        System.out.println("after returing two");
    }
}

方法:

/**
 * @Title: MultiAdvicesAspect
 * @Package org.mouse.spring.aspect
 * @Description: 实现类
 * @author Mahone Wu
 * @date 2017/12/12 15:50
 * @version V1.0
 */
@Service
public class AdviceImpl {

    public boolean execute(String name,String msg){
        System.out.println(msg +","+name);
        return Boolean.TRUE;
    }
}

配置:




    
    
    
    

测试类

/**
 * @Title: MultiAdvicesAspect
 * @Package org.mouse.spring.aspect
 * @Description: test
 * @author Mahone Wu
 * @date 2017/12/12 15:42
 * @version V1.0
 */
public class TestMain {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/aspect/aspect.xml");
        AdviceImpl adviceImpl = (AdviceImpl) ctx.getBean("adviceImpl");
        adviceImpl.execute("mahone", "hello");
    }

}

打印结果


spring aop aspect相关顺序及相关通知使用_第1张图片
图片.png

3.2、各种切面

先看看一个异常:

前置通知:方法名称execute,传入参数:2
Exception in thread "main" org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public boolean org.mouse.spring.aspect.AdviceImpl.execute(java.lang.String,java.lang.String)
    at org.springframework.aop.framework.CglibAopProxy.processReturnType(CglibAopProxy.java:361)
    at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:84)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:657)
    at org.mouse.spring.aspect.AdviceImpl$$EnhancerBySpringCGLIB$$eaf4c242.execute()
    at org.mouse.spring.aspect.TestMain.main(TestMain.java:19)
hello,Mahone
环绕通知:方法:execute,返回结果:true
后置通知:方法名称:execute,after method
返回通知:方法名称:execute,返回结果:null

对于上面的异常,是由于配置的环绕通知没加返回值得缘故,如下代码

@Around(value = "taskExecution()")
    public void aroundException(ProceedingJoinPoint pjp){
        String methodName = pjp.getSignature().getName();
        Object result = null;
        try {
            result = pjp.proceed();
            System.out.println("环绕通知:方法:"+methodName+",返回结果:"+result);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

将void改为Object设置成有返回值方法即可。
下面我们看看各种切面,代码如下

/**
 * @Title: MultiAdvicesAspect
 * @Package org.mouse.spring.aspect
 * @Description: 测试所有的切面advice
 * @author Mahone Wu
 * @date 2017/12/12 16:20
 * @version V1.0
 */
@Component
@Aspect
public class MultiAdvicesAllAspect {

    @Pointcut("execution(boolean *.execute(String,..))")
    public void taskExecution(){}

    /**
     * 前置通知
     */
    @Before("taskExecution()")
    public void before(JoinPoint jp){
        String methodName = jp.getSignature().getName();
        Object[] args = jp.getArgs();
        System.out.println("前置通知:方法名称"+methodName+",传入参数:"+args.length);
    }


    /**
     * 环绕通知
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(value = "taskExecution()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        String methodName = pjp.getSignature().getName();
        Object result = null;
        try {
             result = pjp.proceed();
            System.out.println("环绕通知:方法:"+methodName+",返回结果:"+result);
        } catch (Throwable throwable) {//如果配置了异常通知,则这里需要抛出异常信息
            throw throwable;
        }
        return result;
    }

    /**
     * 后置通知
     * @param jp
     */
    @After("taskExecution()")
    public void after(JoinPoint jp){
        String methodName = jp.getSignature().getName();
        System.out.println("后置通知:方法名称:"+methodName+",after method");
    }

    /**
     * 返回通知
     * @param jp
     * @param result
     */
    @AfterReturning(value = "taskExecution()",returning = "result")
    public void afterReturning(JoinPoint jp,Object result){
        String methodName = jp.getSignature().getName();
        System.out.println("返回通知:方法名称:"+methodName+",返回结果:"+result);
    }

    /**
     * 异常通知
     * @param jp
     * @param ex
     */
    @AfterThrowing(value = "taskExecution()",throwing = "ex")
    public void afterThrowing(JoinPoint jp,Exception ex){
        String methodName = jp.getSignature().getName();
        System.out.println("异常通知,方法名:"+methodName+"发生的异常ex="+ex);
    }

![图片.png](http://upload-images.jianshu.io/upload_images/1456372-5a49833ae630f17a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


这里给出了相关代码,说明两点
(1)、后置通知在返回通知前执行
我按照理解之前觉得返回通知在后置通知之前的,但是实际测试后发现,后置通知在返回通知前执行。

(2)、对于异常通知
如果再环绕通知的时候捕获到异常而没有抛出的话,那么异常通知将没有作用,所以如果配置了异常通知,在环绕通知中捕获的异常需要继续抛出。

给出实现类新增的方法:

/**
 * @Title: MultiAdvicesAspect
 * @Package org.mouse.spring.aspect
 * @Description: 实现类
 * @author Mahone Wu
 * @date 2017/12/12 15:50
 * @version V1.0
 */
@Service
public class AdviceImpl {

    public boolean execute(String name,String msg){
        System.out.println(msg +","+name);
        return Boolean.TRUE;
    }


    public boolean execute(String name,int a){
        int b = 1 / a;
        System.out.println("name="+name);
        return Boolean.TRUE;
    }

    public boolean execute(int a){
        int b = 1 / a;
        return Boolean.TRUE;
    }
}

dsa

你可能感兴趣的:(spring aop aspect相关顺序及相关通知使用)