通知的种类:
1、前置通知
2、后置通知
3、最终通知
4、异常通知
5、环绕通知
在之前的例子中,我们使用到了before(前置通知)和after-returning(后置通知)。还有after最终通知(最终通知)和around(环绕通知)。
前置通知和后置通知已经在前面的帖子涉及到,这里只做总结:
前置通知:
*在目标方法执行前执行
*所以目标方法如果出现异常,这里还会执行
后置通知:
*在目标方法执行后执行
*如果目标方法出现异常,这里不会执行
*可以接收由目标方法的返回值
配置如下:
*注意:这些配置是在标签里面定义的
<!--
aop配置
-->
<aop:config>
<!--
aop:pointcut 指的是切入点判断,我们昨天使用的是在拦截器中使用if方法,具体内容可以看上一篇帖子
但是考虑到如果有很多个dao,那么就要判断很多次,效率会降低
expression 指的是切入点的表达式,该表达式的作用就是调用哪些方法,
一般都使用简写形式,即只需标明 (* *(..)),
第一个*是指返回值类型,第二个是指方法全名
括号中的.. 类似于可变参数的作用,
在这里我们执行的是目标类PersonDaoImpl 的某个方法
id 是指对这个切入点的唯一标识符
-->
<aop:pointcut expression="execution(* cn.ansel.aopExample.PersonDaoImpl.*(..))" id="perform"/>
<!--
aop:aspect 定义切面,因为要把相应的类定义到配置文件中才算是切面
id 该切面的唯一标识符
ref 该切面的引用对象
-->
<aop:aspect id="mytransaction" ref="myTransaction">
<!--
method 指对应切面中的通知,这里的method对应的切面,就是上级标签ref对应的切面
pointcut-ref 指该值对应的切入点表达式
before 指前置通知的配置
-->
<aop:before method="before" pointcut-ref="perform" />
<!--
aop:after-returning 指后置通知的定义标签
returning 指目标方法中返回值的名称
-->
<aop:after-returning method="afterReturning" pointcut-ref="perform" returning="var"/>
</aop:aspect>
</aop:config>
/目标类的代码:
public class PersonDaoImpl extends hibernateUtil {
public String savePerson(Person person) {
System.out.println("targetMethod");
//返回字符串
return "the object form targetMethod";
}
}
//切面中方法:
public class MyTransaction extends hibernateUtil{
public void before(){
System.out.println("beforeMethod");
}
public void afterReturning(Object var){
//接收目标类返回值,并输出
System.out.println(var);
}
}
//测试类的方法:
public class testAOP {
@Test
public void test(){
//加载spring配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("cn/ansel/aopExample/applicationContext.xml");
//得到目标类
PersonDaoImpl dao=(PersonDaoImpl) applicationContext.getBean("personDaoImpl");
//存入数据
Person person=new Person();
person.setPname("ansel");
person.setPdescription("fine");
dao.savePerson(person);
}
}
运行结果:
*主要注意的是:在配置文件中配置的returning参数的名字,必须与切面的后置通知中的传入参数的名字要一致,否则会报错
example:
//我把后置通知中的传入参数名字改成sth,然后再输出sth
public void afterReturning(Object sth){
//接收目标类返回值,并输出
System.out.println(sth);
}
此时,目标方法不变,继续执行测试类的运行结果,有一大堆报错,于是我标注了重要的提示:
最终通知:
直接看配置:在切面中,直接写一个新的方法作为最终通知,然后在配置文件中配置
//最终通知
public void after(){
System.out.println("after");
}
<!--
配置文件中的配置,在刚刚后置通知的后面加上这一行代码
after 指最终通知,其他没有什么不同,需要注意的是,配置每一个通知的时候,都不要忘记了pointcut-ref的引用
-->
<aop:after method="after" pointcut-ref="perform" />
目标方法不变,运行结果:
要看最终通知有什么真正的效果,由于最终的名字,想到了Java中有一个finally代码块,所以我们在目标方法加一个异常:
public class PersonDaoImpl extends hibernateUtil {
public String savePerson(Person person) {
//添加一个异常,用来测试最终通知的作用
int a=1/0;
System.out.println("targetMethod");
//返回字符串
return "the object form targetMethod";
}
}
运行结果:毫无意外的报了异常,可是最终通知运行了,后置通知没有运行,目标方法也没有运行
由此可以看出,最终通知跟finally代码块的作用是一致的,finally也是最终的意思好吧。。
异常通知:
该通知的作用主要用于返回目标方法中出现的异常。配置如下:
在切面中增加这样一个方法:
/**
* 异常通知
* @param ex 参数为目标方法中返回的异常,改名字与配置文件中的throwing配置的名字要一致
* 否则跟上面返回值一样报错
*/
public void throwing(Throwable ex){
System.out.println(ex.getMessage());
}
<!--
配置文件:
aop:after-throwing 指异常通知,接收目标方法发生的异常
throwing 指目标方法抛出异常的引用名
-->
<aop:after-throwing method="throwing" pointcut-ref="perform" throwing="ex"/>
测试类不变,运行结果为:
环绕通知:
用来控制目标方法是否执行。
//ProceedingJoinPoint 该类有一个proceed方法,用来控制目标方法是否执行。 在这里我们先不用那个方法,看看目标方法是否会执行
public void around(ProceedingJoinPoint joinPoint){
System.out.println("around");
}
配置文件:
<!--
aop:around 配置环绕通知
-->
<aop:around method="around" pointcut-ref="perform"/>
运行结果:
在目标方法中有
- 1
- 1
但是没有报错,并且只是执行了前置通知和环绕通知。这时候最终通知也不执行了。
但是当我调用ProceedingJoinPoint类的proceed方法时,这里先把异常代码注释掉:
目标类代码:
public class PersonDaoImpl extends hibernateUtil {
public String savePerson(Person person) {
//添加一个异常,用来测试最终通知的作用
// int a=1/0;
System.out.println("targetMethod");
//返回字符串
return "the object form targetMethod";
}
}
环绕通知的代码该为:
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("around");
//调用proceed方法
joinPoint.proceed();
}
运行结果为:
全部配置的通知都执行了,除了异常通知。