Spring中AOP學習筆記

 

AOP

  • 描述(摘抄百度百科)
  • AOP開發中的相關操作術語
  • 實例(xml方式)
    • 通知的類型
    • 切入點表達式的寫法:
  • 實例(註解方式)

 

描述(摘抄百度百科)

  AOP(面向切面編程):Aspect Oriented Programming的縮寫。意為:面向切面編程,通過預編譯方式和運行期間動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

AOP開發中的相關操作術語

  Joinpoint(連接點):所謂連接點是指那些被攔截到的點。其實就是類裡面可以被增強的方法,這些方法稱為連接點。
  Pointcut(切入點):所謂切入點是指我們要對哪些 Joinpoint 進行攔截的定義。比如實際開發中只對saveAccount()方法增強,該方法就是切入點。
  Advice(通知/增強):增強的邏輯,稱為增強或通知
  Aspect(切面):把增強應用到切入點上面的過程稱為切面。
  Introduction(引介):引介是一種特殊的通知在不修改類程式碼的前提下, Introduction 可以在運行期為類動態地添加一些方法或 Field。
  Target(目標對象):代理的目標對象。
  Proxy(代理):代理對象
  Weaving(織入):將通知應用到目標的過程。例如:將日誌記錄應用到saveAccount()方法上面。
下圖更直觀:
在这里插入图片描述

實例(xml方式)

創建一個meavn工程,pom文件中添加如下依賴:

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
</dependencies>

  

AccountService 介面:

public interface AccountService {

    /***
    * 保存賬戶
    */
    void saveAccount();

    /***
     * 更新賬戶
     */
    void updateAccount(Integer i);

    /***
     * 刪除賬戶
     */
    void deleteAccount(Integer i);
}

  

AccountServiceImpl實現類:

public class AccountServiceImpl implements AccountService {
    public void saveAccount() {
        System.out.println("保存用戶成功.....");
    }

    public void updateAccount(Integer i) {
        System.out.println("更新用戶成功.....");
    }

    public void deleteAccount(Integer i) {
        System.out.println("刪除用戶成功.....");
    }
}

  

再編寫一個增強的類Logger:

public class Logger {
    /*** 
    * @Description: 用於列印日誌
    * @Date: 2021/2/9
    */ 
    public void Printlog(){
        System.out.println("輸出列印日誌.....");
    }
}

  

bean.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
       xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="//www.springframework.org/schema/aop"
       xsi:schemaLocation="//www.springframework.org/schema/beans
       //www.springframework.org/schema/beans/spring-beans.xsd
       //www.springframework.org/schema/aop
       //www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置service-->
    <bean id="accountService" class="com.zy.aop.service.AccountServiceImpl"></bean>

    <!--配置切面logger類-->
    <bean id="logger" class="com.zy.aop.logger.Logger"></bean>

    <!--配置Aop-->
    <aop:config>
        <!--配置切入點表達式-->
        <aop:pointcut id="log" expression="execution(public void com.zy.aop.service.AccountServiceImpl.saveAccount())"/>
        <!--配置切面-->
        <aop:aspect id="" ref="logger">
            <!--前置通知:在切入點方法執行前執行-->
            <aop:before method="Printlog" pointcut-ref="log"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>

  

測試程式碼:

public class TestAop {
    @Test
    public void TestSaveAccount(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        AccountService accountService = (AccountService) context.getBean("accountService");
        accountService.saveAccount();
        accountService.updateAccount(1);
        Integer i = 10;
    }
}

  

運行結果:
在这里插入图片描述
標籤說明:

標籤說明
aop:config 該標籤聲明aop配置開始
aop:pointcut 用於配置切入點表達式。就是指定對哪些類的哪些方法進行增強。屬性:expression:用於定義切入點表達式。id:用於給切入點表達式提供一個唯一標識
aop:aspect 用於配置切面。屬性:id:給切面提供一個唯一標識。ref:引用配置好的通知類 bean 的 id

通知的類型

通知類型標籤
前置通知 <aop:before method="" pointcut-ref=""></aop:before>
後置通知 <aop:after-returning method="" pointcut-ref=""></aop:after-returning>
異常通知 <aop:after-throwing method=""></aop:after-throwing>
最終通知 <aop:after method="" pointcut-ref=""></aop:after>
環繞通知 <aop:around method="" pointcut-ref=""></aop:around>

每一個標籤中都有這三個屬性method:指定通知中方法的名稱。pointct:定義切入點表達式pointcut-ref:指定切入點表達式的引用。
前置通知:

<aop:config>
        <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/>
        <aop:aspect id="" ref="logger">
            <!--前置通知:在切入點方法執行前執行-->
            <aop:before method="Printlog" pointcut-ref="log"></aop:before>
        </aop:aspect>
</aop:config>

  

後置通知:

<aop:config>
        <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/>
        <aop:aspect id="" ref="logger">
            <!--後置通知:在切入點方法執行後執行-->
            <aop:after-returning method="Printlog" pointcut-ref="log"></aop:after-returning>
        </aop:aspect>
</aop:config>

  

異常通知:

<aop:config>
        <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/>
        <aop:aspect id="" ref="logger">
            <!--異常通知:程式出現異常的時候,進行的操作-->
            <aop:after-throwing method="Printlog" pointcut-ref="log"></aop:after-throwing>
        </aop:aspect>
</aop:config>

  

最終通知:

<aop:config>
        <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/>
        <aop:aspect id="" ref="logger">
            <!--最終通知:在切入點方法執行後執行,無論方法是否正常執行都會在後面執行-->
            <aop:after method="Printlog" pointcut-ref="log"></aop:after>
        </aop:aspect>
</aop:config>

  

環繞通知:

<aop:config>
        <aop:pointcut id="log" expression="execution(* com.zy.aop.service.AccountServiceImpl.saveAccount())"/>
        <aop:aspect id="" ref="logger">
            <!--環繞通知-->
            <aop:around method="Printlog2" pointcut-ref="log"></aop:around>
        </aop:aspect>
</aop:config>

  

我們要修改一下增強的類Logger中的方法:

public class Logger {
    /***
     * @Description: 用於列印日誌
     * @Date: 2021/2/9
     */
    public void Printlog() {
        System.out.println("輸出列印日誌.....");
    }
    public Object Printlog2(ProceedingJoinPoint pro) {
        Object proceed = null;
        Object[] args = pro.getArgs();//得到方法實行所需參數
        try {
            //在這之前是前置通知
            proceed = pro.proceed(args);//調用業務層方法
            //在這之後是後置通知
            return proceed;
        } catch (Throwable t) {
            //在這是異常通知
        } finally {
            //在這是最終通知
        }
        return proceed;
    }
}

  

切入點表達式的寫法:

表達式語法:execution([修飾符] 返回值類型 包名.類名.方法名(參數))
寫法說明:
全匹配方式:public void com.zy.aop.service.AccountServiceImpl.saveAccount(參數類型)
訪問修飾符可以省略: void com.zy.aop.service.AccountServiceImpl.saveAccount(參數類型)
返回值可以使用號,表示任意返回值: com.zy.aop.service.AccountServiceImpl.saveAccount(參數類型),具體可以百度在這我就不寫了。
通常情況下,我們都是對業務層的方法進行增強,所以切入點表達式都是切到業務層實現類。
execution(* com.zy.aop.service..(…))

Tags: