0.五大通知
前置通知:在切入點方法執行之前執行;
後置通知:在切入點方法正常執行之後執行。它和異常通知永遠只能執行一個;
異常通知:在切入點方法執行產生異常之後執行。它和後置通知永遠只能執行一個;
最終通知:無論切入點方法是否正常執行它都會在其後面執行;
環繞通知:內含4個通知的寫法,一般用環繞通知,因爲結果有點不同。
- Spring框架爲我們提供了一個接口:ProceedingJoinPoint。該接口有一個方法proceed(),此方法就相當於明確調用切入點方法。
- 該接口可以作爲環繞通知的方法參數,在程序執行時,spring框架會爲我們提供該接口的實現類供我們使用。
- 環繞通知裏有切入點方法和4個通知類型,如圖
1.bean.xml的配置
- (1)在aop:pointcut標籤內寫切入點表達式(公共切入面),下面的各種通知類型可以調用該切入點表達式。
- (2)在aop:aspect標籤內寫通知類型和通知方法,頂部寫通知類來源。
<?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">
<!--配置spring的IOC,把service對象配置進來-->
<bean id="accountService" class="com.jh.service.impl.AccountServiceImpl"></bean>
<!--配置Logger類-->
<bean id="logger" class="com.jh.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!-- 配置切入點簡便表達式:id屬性用於指定表達式的唯一標識。expression屬性用於指定表達式內容
此標籤寫在aop:aspect標籤內部只能當前切面使用。
它還可以寫在aop:aspect外面,此時就變成了所有切面可用,必須寫在前面(配置AOP的順序)
-->
<aop:pointcut id="pt1" expression="execution(* com.jh.service.impl.*.*(..))"></aop:pointcut>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!--配置前置通知:在切入點方法執行之前執行-->
<aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>
<!--配置後置通知:在切入點方法正常執行之後執行。它和異常通知永遠只能執行一個-->
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>
<!--配置異常通知:在切入點方法執行產生異常之後執行。它和後置通知永遠只能執行一個-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
<!--配置最終通知:無論切入點方法是否正常執行它都會在其後面執行-->
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
<!-- 配置環繞通知 詳細的註釋請看Logger類中-->
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
</aop:aspect>
</aop:config>
</beans>
如圖:
2. 各層相對於XML配置的變化
service:接口無變化
service:實現類
package com.jh.service.impl;
import com.jh.service.IAccountService;
/**
* 賬戶的業務層實現類
* */
public class AccountServiceImpl implements IAccountService {
@Override
public void saveAccount() {
System.out.println("執行了保存");
//int i=1/0;
/**結果:
* 前置通知Logger類中的beforePrintLog方法開始記錄日誌了。。。
* 執行了保存
* 異常通知Logger類中的afterThrowingPrintLog方法開始記錄日誌了。。。
* 最終通知Logger類中的afterPrintLog方法開始記錄日誌了。。。
* */
}
@Override
public void updateAccount(int i) {
System.out.println("執行了更新操作:"+i);
}
@Override
public int deleteAccount() {
System.out.println("執行了刪除");
return 0;
}
}
當 System.out.println(“執行了保存”);下面存在異常語句int i=1/0;時,輸出語句正常執行。
結果爲:(異常通知和後置通知不能共存)
前置通知Logger類中的beforePrintLog方法開始記錄日誌了。。。
執行了保存
異常通知Logger類中的afterThrowingPrintLog方法開始記錄日誌了。。。
最終通知Logger類中的afterPrintLog方法開始記錄日誌了。。。
utils:Logger通知類
package com.jh.utils;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 用於記錄日誌的工具類,它裏面提供了公共的代碼
* */
public class Logger {
/**前置通知*/
public void beforePrintLog(){
System.out.println("前置通知Logger類中的beforePrintLog方法開始記錄日誌了。。。");
}
/**後置通知*/
public void afterReturningPrintLog(){
System.out.println("後置通知Logger類中的afterReturningPrintLog方法開始記錄日誌了。。。");
}
/**異常通知*/
public void afterThrowingPrintLog(){
System.out.println("異常通知Logger類中的afterThrowingPrintLog方法開始記錄日誌了。。。");
}
/**最終通知*/
public void afterPrintLog(){
System.out.println("最終通知Logger類中的afterPrintLog方法開始記錄日誌了。。。");
}
/**
* 環繞通知
* 問題:當我們配置了環繞通知之後,切入點方法沒有執行,而通知方法執行了。
* 分析:
*通過對比動態代理中的環繞通知代碼,發現動態代理的環繞通知有明確的切入點方法調用,而我們的代碼中沒有。
* 解決:
* Spring框架爲我們提供了一個接口:ProceedingJoinPoint。該接口有一個方法proceed(),此方法就相當於明確調用切入點方法。
*該接口可以作爲環繞通知的方法參數,在程序執行時,spring框架會爲我們提供該接口的實現類供我們使用。
* spring中的環繞通知:
* 它是spring框架爲我們提供的一種可以在代碼中手動控制增強方法何時執行的方式。
* */
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object rtValue=null;
try {
Object[] args=pjp.getArgs();//得到方法執行所需的參數
System.out.println("Logger類中的aroundPrintLog方法開始記錄日誌了。。。前置");
rtValue=pjp.proceed(args);//明確調用業務層方法(切入點方法)
System.out.println("Logger類中的aroundPrintLog方法開始記錄日誌了。。。後置");
return rtValue;
} catch (Throwable t) {
System.out.println("Logger類中的aroundPrintLog方法開始記錄日誌了。。。異常");
throw new RuntimeException(t);
} finally {
System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。最終");
}
}
}
一般只用環繞通知的結果:
Logger類中的aroundPrintLog方法開始記錄日誌了。。。前置
執行了保存
Logger類中的aroundPrintLog方法開始記錄日誌了。。。後置
Logger類中的aroundPringLog方法開始記錄日誌了。。。最終
測試類AOPTest
package com.jh.test;
import com.jh.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**測試AOP的配置*/
public class AOPTest {
public static void main(String[] args) {
//1.獲取容器
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
//2.根據id獲取service bean對象
IAccountService as=(IAccountService)ac.getBean("accountService");
//3.執行方法
as.saveAccount();
}
}
手寫四個結果:(一般只執行3個,後置通知和異常通知不能同時存在)
前置通知Logger類中的beforePrintLog方法開始記錄日誌了。。。
執行了保存
最終通知Logger類中的afterPrintLog方法開始記錄日誌了。。。
後置通知Logger類中的afterReturningPrintLog方法開始記錄日誌了。。。
發現與環繞通知的四個結果不同:
環繞通知是最終通知在最後,一般也是用環繞通知。