1. 程式人生 > 實用技巧 >Spring(8)使用XML配置開發Spring AOP(二)

Spring(8)使用XML配置開發Spring AOP(二)

一、環境準備

  前面我們利用賬戶轉賬的例子進行了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實現原理基於動態代理,在多個代理模式下,按照責任鏈模式執行。簡單圖示如下: