為了搞明白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,具體的一些知識點,後續會分享出來~
呼呼~~下班啦