为了搞明白AOP通知的类型之间有什么区别,我也是走了一些小弯路,下面就把我遇见的坑坑洼洼扒拉出来凉一凉吧~
一、AOP的通知类型
1.前置通知(before advice):在连接点前面执行,对连接点不会造成影响(前置通知有异常的话,会对后续操作有影响)
2.正常返回通知(after returning advice):在连接点正确执行之后执行,如果连接点抛异常,则不执行
3.异常返回通知(after throw Advice):在连接点抛异常的时候执行
4.返回通知(after):无论连接点是正确执行还是抛异常,都会执行
5.环绕通知(around):在连接点前后执行(必须在环绕通知中决定是继续处理还是中断执行,使用PreceedingJoinPonit下的方法决定是继续还是中断)
注:这里我不得不说一下了,上面第五条中红色标注的地方非常非常非常重要,菜鸟(哦不,可能笨鸟更适合我)就在这翻了一个很大的跟头
二、两种实现方式
方法一:配置文件法
1.业务类
package lm.practice.services; /** * Created by Administrator on 2017/4/13. */ /** * 业务实现类 */ public class BankService { /** * 模拟银行转账系统 * @param form 转出者 * @param to 转入者 * @param count 金额 * @return */ public boolean transfer(String form, String to, double count) { if(count<100.0){ throw new IllegalArgumentException("最低转账金额不得少于100"); } System.out.println(form+"向"+to+"中行账户转账"+count); return false; } }
2.切面类
package lm.practice.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /** * Created by Administrator on 2017/4/13. */ /** * 切面类 */ public class MyAdvices { /** * 前置通知 * @param joinPoint 连接点 */ public void before(JoinPoint joinPoint){ System.out.print("--------前置通知----------"); } /** * 正常返回通知 * @return */ public boolean afterReturning(){ System.out.println("------正常返回通知--------"); return true; } /** * 异常返回通知 * @return */ public void afterThrow(){ System.out.print("-------异常返回通知-------"); } /** * 返回通知 * @return */ public void after(){ System.out.println("----------返回通知--------"); } /** * 环绕通知 * @return */ public boolean around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("--------环绕通知----------"); oinPoint.proceed(); return true; } }
3.配置文件
<?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"> <!--被代理对象--> <bean id="bankService" class="lm.practice.services.BankService"> </bean> <!--通知--> <bean id="myAdvices" class="lm.practice.aop.MyAdvices"></bean> <!--aop配置--> <aop:config proxy-target-class="true"> <!--切面--> <aop:aspect ref="myAdvices"> <!--切点--> <aop:pointcut id="pointcut" expression="execution(* lm.practice.services.*.*(..))"></aop:pointcut> <!--连接通知方法与切点--> <aop:before method="before" pointcut-ref="pointcut"></aop:before> <aop:after-returning method="afterReturning" pointcut-ref="pointcut"></aop:after-returning> <aop:after-throwing method="afterThrow" pointcut-ref="pointcut"></aop:after-throwing> <aop:after method="after" pointcut-ref="pointcut"></aop:after> <aop:around method="around" pointcut-ref="pointcut"></aop:around> </aop:aspect> </aop:config> </beans>
4.测试类
package test; import lm.practice.services.BankService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * Created by Administrator on 2017/4/13. */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-aop-bank.xml") public class BankServiceTest { @Autowired private BankService bankService; @Test public void test(){ bankService.transfer("商行","中行",100.00); }
4.运行结果
如果转账金额小于100的情况下,输出结果:
方法二、注解法
可以参照我之前写的博客自己试着进行
-------------------------------------------------------------------------以上是正确的代码---------------------------------------------------------
我现在就说一下我犯的一个错误:
package lm.practice.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /** * Created by Administrator on 2017/4/13. */ /** * 切面类 */ public class MyAdvices { /** * 前置通知 * @param joinPoint 连接点 */ public void before(JoinPoint joinPoint){ System.out.print("--------前置通知----------"); } /** * 正常返回通知 * @return */ public boolean afterReturning(){ System.out.println("------正常返回通知--------"); return true; } /** * 异常返回通知 * @return */ public void afterThrow(){ System.out.print("-------异常返回通知-------"); } /** * 返回通知 * @return */ public void after(){ System.out.println("----------返回通知--------"); } /** * 环绕通知 */ public void around(){ System.out.println("--------环绕通知----------"); }}
这是我犯错的时候的切面类的书写方法,报错信息如下:
org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public boolean lm.practice.services.BankService.transfer(java.lang.String,java.lang.String,double)
上面的就是控制台中那段很长的代码:通知的返回为空,与注入方法返回类型不匹配。
-------------------------------------------------------我的漫漫求解路----------------------------------------------------------
第一次尝试:首先我以为单纯的是返回类型的问题,就把切面类中的异常返回通知(afterReturning)返回值类型改成了boolen,结果不可行,依旧报错
第二次尝试:将不同通知类型分开写(没有完成,被导师否定,因为根源不在这)
第三次尝试:将配置文件的方式改成注解,还是不行
第四次尝试:导师捉急啊,直接一步一步引导我,说要抛开表面看实质,他是这样引导我的:首先问我每个通知类型的区别,然后问我around和after和before有什么区别,然后问我知道around有什么注意点吗,最后让我查一下,既然是around(环绕),那么怎么确定是不是会执行连接点呢?最后我查了一下,发现在使用around的时候必须要决定是继续执行还是中断执行,然后我这只笨鸟就在around中写了那么一段继续执行的代码,然后问题解决了
总结一下就是around必须要指定是继续执行还是中断执行(重要!重要!!重要!!!重要!!!!!)
到此我的spring学习就告一段落了,这里还有一个重要的知识点就是日志了,我这里用到的是log4j,具体的一些知识点,后续会分享出来~
呼呼~~下班啦