Spring(8)使用XML配置開發Spring AOP(二)
阿新 • • 發佈:2020-08-18
一、環境準備
前面我們利用賬戶轉賬的例子進行了Spring IOC的講解,我們在學習 spring 的 aop 時,也採用賬戶轉賬作為示例,並且把 spring 的 ioc 也一起應用進來。
1.程式碼準備
實體類、介面及實現類我們沿用之前的程式碼。
2.引入必要的Maven依賴
<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.xhbjava</groupId> <artifactId>Spring02</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version></dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies> </project>
3.建立spring 的配置檔案並匯入約束
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns="http://www.springframework.org/schema/beans" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>
4.配置Spring的IOC
<!-- 注入切面 --> <bean id="xmlAspect" class="com.xhbjava.aspect.XmlAspect"/> <bean id="accountService" class="com.xhbjava.service.impl.AccountServiceImpl"/>
5.切面類
package com.xhbjava.aspect; public class XmlAspect { public void before() { System.out.println("before...."); } public void after() { System.out.println("after...."); } public void afterThrowing() { System.out.println("after-throwing...."); } public void afterReturning() { System.out.println("after-returning...."); } }
二、配置
1.前置通知、後置通知、返回通知和異常通知配置
<!-- 配置AOP --> <aop:config> <!-- 引入切面 --> <aop:aspect ref="xmlAspect"> <!-- 定義通知 --> <aop:before method="before" pointcut="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..))" /> <aop:after method="after" pointcut="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..))" /> <aop:after-throwing method="afterThrowing" pointcut="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..))" /> <aop:after-returning method="afterReturning" pointcut="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..))" /> </aop:aspect> </aop:config>
2.測試
package com.xhbjava.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.xhbjava.pojo.Account; import com.xhbjava.service.IAccountService; public class testSpring { private ApplicationContext ctx; @Test public void testDelete() { ctx = new ClassPathXmlApplicationContext("bean.xml"); Account account = new Account(); account.setId(4); account.setName("張三1"); account.setMmoney(6500f); IAccountService accountService = ctx.getBean(IAccountService.class); // 3.執行方法 accountService.printAccount(account); } }
三、改進
1.定義切點
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns="http://www.springframework.org/schema/beans" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 注入切面 --> <bean id="xmlAspect" class="com.xhbjava.aspect.XmlAspect" /> <bean id="accountService" class="com.xhbjava.service.impl.AccountServiceImpl" /> <!-- 配置AOP --> <aop:config> <!-- 引入切面 --> <aop:aspect ref="xmlAspect"> <!-- 定義切點 --> <aop:pointcut expression="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..))" id="printAccount" /> <!-- 定義通知 引入切點 --> <aop:before method="before" pointcut-ref="printAccount" /> <aop:after method="after" pointcut-ref="printAccount" /> <aop:after-throwing method="afterThrowing" pointcut-ref="printAccount" /> <aop:after-returning method="afterReturning" pointcut-ref="printAccount" /> </aop:aspect> </aop:config> </beans>
2.有點
通過定義切點並引入,這樣避免了重複程式碼。
四、環繞通知
1.環繞通知
和其他通知一樣,環繞通知也可以織入到特定的流程中,我們在之前的切面類中新增如下程式碼:
public void around(ProceedingJoinPoint jp) { System.out.println("around before..."); try { jp.proceed(); } catch (Throwable t) { new RuntimeException("回撥原有流程,產生異常。。。"); } System.out.println("around after..."); }
<aop:around method="around" pointcut-ref="printAccount"/>
2.給通知傳遞引數
(1)改造切面類方法
public void before(Account account) { System.out.println("before...."); System.out.println("Account:"+account); }
(2)改造Spring 中的bean.xml配置檔案:
<aop:before method="before" pointcut="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..)) and args(account)" />
五、多切面
我們前面的例子中只有一個切面,而事實是 Spring 也能支援多個切面。有多個切面時,在測試過程中發現它不會存在任何順序,這些順序 碼會隨機生成,但是有時候我們希望它按照指定的順序執行。我們下面通過XML配置形式學習講解下。1.在原來的例子基礎上增加切面
package com.xhbjava.aspect; import org.aspectj.lang.ProceedingJoinPoint; import com.xhbjava.pojo.Account; public class XmlAspect { public void before(Account account) { System.out.println("before0...."); System.out.println("Account:"+account); } public void after() { System.out.println("after0...."); } public void afterThrowing() { System.out.println("after-throwing0...."); } public void afterReturning() { System.out.println("after-returning0...."); } public void around(ProceedingJoinPoint jp) { System.out.println("around before0..."); try { jp.proceed(); } catch (Throwable t) { new RuntimeException("回撥原有流程,產生異常。。。"); } System.out.println("around after0..."); } }
package com.xhbjava.aspect; import org.aspectj.lang.ProceedingJoinPoint; import com.xhbjava.pojo.Account; public class XmlAspect1 { public void before(Account account) { System.out.println("before1...."); System.out.println("Account:"+account); } public void after() { System.out.println("after1...."); } public void afterThrowing() { System.out.println("after-throwing1...."); } public void afterReturning() { System.out.println("after-returning1...."); } public void around(ProceedingJoinPoint jp) { System.out.println("around before1..."); try { jp.proceed(); } catch (Throwable t) { new RuntimeException("回撥原有流程,產生異常。。。"); } System.out.println("around after1..."); } }
package com.xhbjava.aspect; import org.aspectj.lang.ProceedingJoinPoint; import com.xhbjava.pojo.Account; public class XmlAspect2 { public void before(Account account) { System.out.println("before2...."); System.out.println("Account:"+account); } public void after() { System.out.println("after2...."); } public void afterThrowing() { System.out.println("after-throwing2...."); } public void afterReturning() { System.out.println("after-returning2...."); } public void around(ProceedingJoinPoint jp) { System.out.println("around before2..."); try { jp.proceed(); } catch (Throwable t) { new RuntimeException("回撥原有流程,產生異常。。。"); } System.out.println("around after2..."); } }
2.配置bean.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xmlns="http://www.springframework.org/schema/beans" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 注入切面 --> <bean id="xmlAspect" class="com.xhbjava.aspect.XmlAspect" /> <bean id="xmlAspect1" class="com.xhbjava.aspect.XmlAspect1" /> <bean id="xmlAspect2" class="com.xhbjava.aspect.XmlAspect2" /> <bean id="accountService" class="com.xhbjava.service.impl.AccountServiceImpl" /> <!-- 配置AOP --> <aop:config> <!-- 引入切面 --> <aop:aspect ref="xmlAspect"> <!-- 定義切點 --> <aop:pointcut expression="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..))" id="printAccount" /> <!-- 定義通知 引入切點 --> <aop:before method="before" pointcut="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..)) and args(account)" /> <aop:after method="after" pointcut-ref="printAccount" /> <aop:after-throwing method="afterThrowing" pointcut-ref="printAccount" /> <aop:after-returning method="afterReturning" pointcut-ref="printAccount" /> <aop:around method="around" pointcut-ref="printAccount" /> </aop:aspect> <aop:aspect ref="xmlAspect1"> <!-- 定義切點 --> <aop:pointcut expression="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..))" id="printAccount1" /> <!-- 定義通知 引入切點 --> <aop:before method="before" pointcut="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..)) and args(account)" /> <aop:after method="after" pointcut-ref="printAccount1" /> <aop:after-throwing method="afterThrowing" pointcut-ref="printAccount1" /> <aop:after-returning method="afterReturning" pointcut-ref="printAccount1" /> <aop:around method="around" pointcut-ref="printAccount1" /> </aop:aspect> <aop:aspect ref="xmlAspect2"> <!-- 定義切點 --> <aop:pointcut expression="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..))" id="printAccount2" /> <!-- 定義通知 引入切點 --> <aop:before method="before" pointcut="execution(* com.xhbjava.service.impl.AccountServiceImpl.printAccount(..)) and args(account)" /> <aop:after method="after" pointcut-ref="printAccount2" /> <aop:after-throwing method="afterThrowing" pointcut-ref="printAccount2" /> <aop:after-returning method="afterReturning" pointcut-ref="printAccount2" /> <aop:around method="around" pointcut-ref="printAccount" /> </aop:aspect> </aop:config> </beans>
3.測試
我們可以看出多個切面是無序的 其執行順序值得我們探討。
4.有序
我們在xml配置中加入排序:
<aop:aspect ref="xmlAspect1" order="2"> ... </aop:aspect >
測試:
5.分析
通過上面測試,我們可以看到多切面按照順序執行,我們知道Spring AOP實現原理基於動態代理,在多個代理模式下,按照責任鏈模式執行。簡單圖示如下: