一、好言
有时候,上天没有给你要的,不是因为你不配,而是因为你指的更好的。
二、背景
最近看看《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;
}
}
配置:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="org.mouse.spring.aspect"></context:component-scan>
<!-- 自动为切面方法中匹配的方法所在的类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
测试类
/**
* @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");
}
}
打印结果
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(<generated>)
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