Spring中基于XML的AOP配置詳解
1. 準備工作
1.1 創(chuàng)建工程 day03_eesy_03SpringAOP
1.2 在配置文件pom.xml中添加依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>day03_eesy_03springAOP</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> </dependencies> </project>
說明: aspect依賴是用來聲明切入點坐標的
1.3 編寫業(yè)務層代碼
1.創(chuàng)建包 service
2.創(chuàng)建業(yè)務層接口IAccountService.java
/**
* 賬戶的業(yè)務層接口
*/
public interface IAccountService {
/**
* 模擬保存賬戶
*/
void saveAccount();
/**
* 模擬更新賬戶
*/
void updateAccount(Integer i);
/**
* 模擬刪除賬戶
*/
int deleteAccount();
}
3.創(chuàng)建業(yè)務層實現(xiàn)類AccountServiceImpl.java
/**
* 賬戶的業(yè)務層實現(xiàn)類
*/
public class AccountServiceImpl implements IAccountService {
public void saveAccount() {
System.out.println("執(zhí)行了保存");
}
public void updateAccount(Integer i) {
System.out.println("執(zhí)行力更新");
}
public int deleteAccount() {
System.out.println("執(zhí)行了刪除");
return 0;
}
}
4.創(chuàng)建日志類
該類為用于記錄日志的工具類,它里面提供了公共的代碼(通知)
Logger.java
/**
* 用于記錄日志的工具類,它里面提供了公共的代碼
*/
public class Logger {
/**
* 用于打印日志,計劃讓其在切入點方法執(zhí)行之前執(zhí)行(切入點方法就是業(yè)務層方法)
*/
public void printLog(){
System.out.println("Logger類中的printLog方法開始記錄日志了");
}
}
2. 進行配置
創(chuàng)建配置文件 bean.xml
先添加包含AOP的約束,后添加配置
<?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.itheima.service.impl.AccountServiceImpl"></bean>
<!--Spring中基于XML的AOP配置步驟
1.把通知Bean也交給Spring來管理
2.使用aop:config標簽表明開始AOP的配置
3.使用aop:aspect標簽表明配置切面
id屬性: 是給切面提供唯一標識
ref屬性: 是指定通知類bean的id
4.在aop:aspect標簽的內(nèi)部使用對應的標簽來配置通知的類型
我們現(xiàn)在的示例是讓Logger類的printLog方法在切入點方法執(zhí)行之前執(zhí)行, 所以是前置通知
aop:before : 表示配置前置通知
method屬性: 用于指定Logger類中哪個方法是前置通知
pointcut屬性: 用于指定切入點表達式,該表達式的含義指的是對業(yè)務層中哪些方法增強
切入點表達式的寫法:
關(guān)鍵字: execution(表達式)
表達式:
訪問修飾符 返回值 包名.包名.包名...類名.方法名(參數(shù)列表)
標準的切入點表達式:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
-->
<!--配置Logger類-->
<bean id="logger" class="com.itheima.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!--配置通知的類型,并且建立通知方法和接入點方法的關(guān)聯(lián)-->
<aop:before method="printLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
說明: 切入點表達式最好按軟件的提示寫,自己直接手寫在測試時容易報錯
3. 創(chuàng)建測試類AOPTest.java
/**
* 測試AOP的配置
*/
public class AOPTest {
public static void main(String[] args) {
//1.獲取容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//2.獲取對象
IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
accountService.saveAccount();
}
}
4. 運行結(jié)果

5. 目錄結(jié)構(gòu)

6. 切入點表達式寫法補充
6.1 介紹
<!-- 切入點表達式的寫法:
關(guān)鍵字: execution(表達式)
表達式:
訪問修飾符 返回值 包名.包名.包名...類名.方法名(參數(shù)列表)
標準的切入點表達式:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
訪問修飾符可以省略:
void com.itheima.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值
* com.itheima.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包,但是又幾級包,就需要寫幾個 *.
* *.*.*.*.AccountServiceImpl.saveAccount()
包名可以使用..表示當前包及其子包
* *..AccountServiceImpl.saveAccount()
類名和方法名都可以使用*來實現(xiàn)通配
* *..*.*()
參數(shù)列表:
可以直接寫數(shù)據(jù)類型:
基本類型直接寫名稱 int
引用類型寫包名.類名的方式 java.lang.String
可以使用通配符表示任意類型,但是必須有參數(shù)
可以是使用..表示有無參數(shù)均可,有參數(shù)可以是任意類型
全通配寫法:
* *..*.*(..)
實際開發(fā)中切入點表達式的通常寫法:
切到業(yè)務層實現(xiàn)類下的所有的方法
* com.itheima.service.impl.*.*(..)
-->
6.2 在bean.xml中表示
<?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.itheima.service.impl.AccountServiceImpl"></bean>
<!--Spring中基于XML的AOP配置步驟
1.把通知Bean也交給Spring來管理
2.使用aop:config標簽表明開始AOP的配置
3.使用aop:aspect標簽表明配置切面
id屬性: 是給切面提供唯一標識
ref屬性: 是指定通知類bean的id
4.在aop:aspect標簽的內(nèi)部使用對應的標簽來配置通知的類型
我們現(xiàn)在的示例是讓Logger類的printLog方法在切入點方法執(zhí)行之前執(zhí)行, 所以是前置通知
aop:before : 表示配置前置通知
method屬性: 用于指定Logger類中哪個方法是前置通知
pointcut屬性: 用于指定切入點表達式,該表達式的含義指的是對業(yè)務層中哪些方法增強
切入點表達式的寫法:
關(guān)鍵字: execution(表達式)
表達式:
訪問修飾符 返回值 包名.包名.包名...類名.方法名(參數(shù)列表)
標準的切入點表達式:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
訪問修飾符可以省略:
void com.itheima.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值
* com.itheima.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包,但是又幾級包,就需要寫幾個 *.
* *.*.*.*.AccountServiceImpl.saveAccount()
包名可以使用..表示當前包及其子包
* *..AccountServiceImpl.saveAccount()
類名和方法名都可以使用*來實現(xiàn)通配
* *..*.*()
參數(shù)列表:
可以直接寫數(shù)據(jù)類型:
基本類型直接寫名稱 int
引用類型寫包名.類名的方式 java.lang.String
可以使用通配符表示任意類型,但是必須有參數(shù)
可以是使用..表示有無參數(shù)均可,有參數(shù)可以是任意類型
全通配寫法:
* *..*.*(..)
實際開發(fā)中切入點表達式的通常寫法:
切到業(yè)務層實現(xiàn)類下的所有的方法
* com.itheima.service.impl.*.*(..)
-->
<!--配置Logger類-->
<bean id="logger" class="com.itheima.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!--配置通知的類型,并且建立通知方法和接入點方法的關(guān)聯(lián)-->
<aop:before method="printLog" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:before>
</aop:aspect>
</aop:config>
</beans>
6.3 在測試類AOPTest.java中測試
/**
* 測試AOP的配置
*/
public class AOPTest {
public static void main(String[] args) {
//1.獲取容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//2.獲取對象
IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
accountService.saveAccount();
accountService.updateAccount(1);
accountService.deleteAccount();
}
}
6.4 運行結(jié)果

7. 四種通知類型補充
7.1 在Logger.java類中添加方法
/**
* 用于記錄日志的工具類,它里面提供了公共的代碼
*/
public class Logger {
/**
* 前置通知
*/
public void beforePrintLog(){
System.out.println("前置通知:Logger類中的printLog方法開始記錄日志了");
}
/**
* 后置通知
*/
public void afterReturningPrintLog(){
System.out.println("后置通知:Logger類中的printLog方法開始記錄日志了");
}
/**
* 異常通知
*/
public void afterThrowingPrintLog(){
System.out.println("異常通知:Logger類中的printLog方法開始記錄日志了");
}
/**
* 最終通知
*/
public void afterPrintLog(){
System.out.println("最終通知:Logger類中的printLog方法開始記錄日志了");
}
}
7.2 在bean.xml中進行配置
<?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.itheima.service.impl.AccountServiceImpl"></bean> <!--配置Logger類--> <bean id="logger" class="com.itheima.utils.Logger"></bean> <!--配置AOP--> <aop:config> <!--配置切面--> <aop:aspect id="logAdvice" ref="logger"> <!--配置前置通知: 在切入點方法執(zhí)行之前執(zhí)行--> <aop:before method="beforePrintLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"></aop:before> <!--配置后通知: 在切入點方法正常執(zhí)行之后執(zhí)行; 他和異常通知只能執(zhí)行一個--> <aop:after-returning method="afterReturningPrintLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"></aop:after-returning> <!--配置異常通知: 在切入點方法執(zhí)行產(chǎn)生異常之后執(zhí)行; 他和后置通知只能執(zhí)行一個--> <aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"></aop:after-throwing> <!--配置最終通知: 無論切入點方法是否正常執(zhí)行他都會在其后面執(zhí)行--> <aop:after method="afterPrintLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.saveAccount())"></aop:after> </aop:aspect> </aop:config> </beans>
7.3 在測試類AOPTest.java中運行
/**
* 測試AOP的配置
*/
public class AOPTest {
public static void main(String[] args) {
//1.獲取容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//2.獲取對象
IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
accountService.saveAccount();
}
}
7.4 運行結(jié)果

8. 通用化切入點表達式
用于解決在bean.xml文件中配置通知時多次寫切入點表達式的問題
使用 aop:pointcut標簽,在bean.xml中進行配置
8.1 在aop:aspect標簽內(nèi)部使用aop:pointcut
<?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.itheima.service.impl.AccountServiceImpl"></bean> <!--配置Logger類--> <bean id="logger" class="com.itheima.utils.Logger"></bean> <!--配置AOP--> <aop:config> <!--配置切面--> <aop:aspect id="logAdvice" ref="logger"> <!--配置前置通知: 在切入點方法執(zhí)行之前執(zhí)行--> <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before> <!--配置后通知: 在切入點方法正常執(zhí)行之后執(zhí)行; 他和異常通知只能執(zhí)行一個--> <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning> <!--配置異常通知: 在切入點方法執(zhí)行產(chǎn)生異常之后執(zhí)行; 他和后置通知只能執(zhí)行一個--> <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing> <!--配置最終通知: 無論切入點方法是否正常執(zhí)行他都會在其后面執(zhí)行--> <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after> <!--配置切入點表達式 id屬性用于指定表達式的唯一標識, expression屬性用于指定表達式的內(nèi)容 此標簽寫在app:aspect標簽內(nèi)部,只能當前切面使用。 它還可以寫在aop:aspect外面, 此時就變成了所有切面可用 --> <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.AccountServiceImpl.*(..))"></aop:pointcut> </aop:aspect> </aop:config> </beans>
運行結(jié)果:

此時,aop:pointcut定義的切入點表達式只能在當前切面中使用
8.2 在aop:aspect標簽外部使用aop:pointcut
<?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.itheima.service.impl.AccountServiceImpl"></bean> <!--配置Logger類--> <bean id="logger" class="com.itheima.utils.Logger"></bean> <!--配置AOP--> <aop:config> <!--配置切入點表達式 id屬性用于指定表達式的唯一標識, expression屬性用于指定表達式的內(nèi)容 此標簽寫在app:aspect標簽內(nèi)部,只能當前切面使用。 它還可以寫在aop:aspect外面, 此時就變成了所有切面可用 --> <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.AccountServiceImpl.*(..))"></aop:pointcut> <!--配置切面--> <aop:aspect id="logAdvice" ref="logger"> <!--配置前置通知: 在切入點方法執(zhí)行之前執(zhí)行--> <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before> <!--配置后通知: 在切入點方法正常執(zhí)行之后執(zhí)行; 他和異常通知只能執(zhí)行一個--> <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning> <!--配置異常通知: 在切入點方法執(zhí)行產(chǎn)生異常之后執(zhí)行; 他和后置通知只能執(zhí)行一個--> <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing> <!--配置最終通知: 無論切入點方法是否正常執(zhí)行他都會在其后面執(zhí)行--> <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after> </aop:aspect> </aop:config> </beans>
運行結(jié)果:

此時所有的切面都能使用該aop:pointcut定義的切入點表達式
主要: 當在aop:aspect標簽外部使用aop:pointcut標簽定義切入點表達式的時候,由于使用的約束的規(guī)定, aop:pointcut標簽只能在 aop:aspect標簽上方
9. Spring的環(huán)繞通知
9.1 在Logger.java中添加實現(xiàn)環(huán)繞通知的方法 aroundPringLog
/**
* 用于記錄日志的工具類,它里面提供了公共的代碼
*/
public class Logger {
/**
* 環(huán)繞通知
*
*/
public void aroundPringLog(){
System.out.println("Logger類中的aroundPringLog方法開始記錄日志了");
}
}
9.2 在bean.xml中完成配置
<?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.itheima.service.impl.AccountServiceImpl"></bean> <!--配置Logger類--> <bean id="logger" class="com.itheima.utils.Logger"></bean> <!--配置AOP--> <aop:config> <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.AccountServiceImpl.*(..))"></aop:pointcut> <!--配置切面--> <aop:aspect id="logAdvice" ref="logger"> <!--配置環(huán)繞通知 詳細的注釋在Logger類中--> <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around> </aop:aspect> </aop:config> </beans>
9.3 此時運行結(jié)果

9.4 問題分析
此時只執(zhí)行了通知方法,而切入點方法沒有執(zhí)行
原因:
通過對比動態(tài)的代理中的環(huán)繞通知代碼,發(fā)現(xiàn)動態(tài)代理的環(huán)繞通知有明確的切入點方法調(diào)用,而本案例中的代碼沒有
9.5 完善aroundPringLog方法
Logger.java
/**
* 用于記錄日志的工具類,它里面提供了公共的代碼
*/
public class Logger {
/**
* 環(huán)繞通知
* 問題:
* 當我們配置了環(huán)繞通知之后,切入點方法沒有執(zhí)行,而通知方法執(zhí)行了
* 分析:
* 通過對比動態(tài)的代理中的環(huán)繞通知代碼,發(fā)現(xiàn)動態(tài)代理的環(huán)繞通知有明確的切入點方法調(diào)用,而我們的代碼中沒有
* 解決:
* Spring框架為我們提供了一個接口,ProceedingJoinPoint,該接口有一個方法proceed(),此方法就相當于明確調(diào)用切入點方法
* 該接口可以作為環(huán)繞通知的方法參數(shù), 在程序執(zhí)行的時候,Spring框架會為我們提供該接口供我們使用
*
* Spring的環(huán)繞通知:
* 他是Spring框架為我們提供的一種可以在代碼中手動控制增強方法何時執(zhí)行的方式
*/
public Object aroundPringLog(ProceedingJoinPoint proceedingJoinPoint){
Object returnValue = null;
try {
Object[] args = proceedingJoinPoint.getArgs(); //得到方法執(zhí)行所需要的參數(shù)
System.out.println("Logger類中的aroundPringLog方法開始記錄日志了-----前置");
returnValue = proceedingJoinPoint.proceed(args); //明確調(diào)用業(yè)務層方法,切入點方法
System.out.println("Logger類中的aroundPringLog方法開始記錄日志了-----后置");
return returnValue;
}catch (Throwable throwable){
System.out.println("Logger類中的aroundPringLog方法開始記錄日志了-----異常");
throw new RuntimeException(throwable);
}finally {
System.out.println("Logger類中的aroundPringLog方法開始記錄日志了-----最終");
}
}
}
9.6 運行結(jié)果

9.7 目錄結(jié)構(gòu)

到此這篇關(guān)于Spring中基于XML的AOP配置的文章就介紹到這了,更多相關(guān)Sprin AOP配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SSM框架中entity mapper dao service controll
這篇文章主要介紹了SSM框架中entity mapper dao service controller層的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
詳解SpringBoot基礎(chǔ)之banner玩法解析
SpringBoot項目啟動時會在控制臺打印一個默認的啟動圖案,這個圖案就是我們要講的banner,這篇文章主要介紹了SpringBoot基礎(chǔ)之banner玩法解析,感興趣的小伙伴們可以參考一下2019-04-04
Spring Cloud Alibaba 使用 Feign+Sentinel 完成熔斷的示例
這篇文章主要介紹了Spring Cloud Alibaba 使用 Feign+Sentinel 完成熔斷的示例,幫助大家更好的理解和學習使用Spring Cloud,感興趣的朋友可以了解下2021-03-03
使用spring通過aop獲取方法參數(shù)和參數(shù)值
這篇文章主要介紹了使用spring通過aop獲取方法參數(shù)和參數(shù)值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
java求100之內(nèi)的素數(shù)(質(zhì)數(shù))簡單示例
這篇文章主要介紹了java求100之內(nèi)的素數(shù)簡單示例,素數(shù)是一個大于1的自然數(shù),如果除了1和它自身外,不能被其他自然數(shù)整除的數(shù);否則稱為合數(shù)2014-04-04
總結(jié)Java集合類操作優(yōu)化經(jīng)驗
本文主要介紹的就是集合框架的使用經(jīng)驗,告訴大家如何高效、方便地管理對象,所有代碼基于JDK7,需要的朋友可以參考下2015-08-08
Springboot與vue實例講解實現(xiàn)前后端分離的人事管理系統(tǒng)
這篇文章主要介紹了如何用Java實現(xiàn)企業(yè)人事管理系統(tǒng),文中采用springboot+vue實現(xiàn)前后端分離,感興趣的小伙伴可以學習一下2022-06-06

