Spring5(10)- AOP (Aspect Oriented Programming) 面向切面編程

1 AOP

簡單地說,就是把程序重複的代碼抽取出來,在需要執行的時候,使用動態代理的技術,在不修改源碼的基礎上,對已有的方法增強。

1.1 AOP術語

1.1.1 Joinpoint 連接點

指被攔截到的點。在spring中,這些點指的是方法,因爲 spring 只支持方法類型的連接點。

1.1.2 Pointcut 切入點

指要對哪些 Jointpoint 進行攔截的定義。

1.1.3 Advice 通知/增強

  • 指攔截到 jointpoint 之後所要做的事情
  • 通知類型:前置通知、後置通知、異常通知、最終通知、環繞通知
  • 在環繞通知中有明確的切入點方法調用
    在這裏插入圖片描述

1.1.4 Introduction引介

一種特使的通知,在不修改類代碼的前提下,Introduction可以在運行期位類動態地添加一些方法或者 field

1.1.5 target 目標對象

代理的目標對象

1.1.6 weaving 織入

指把增強應用到目標對象來創建新的代理對象的過程。

  • spring 採用動態代理織入,而 aspectJ採用編譯期織入和類裝載期織入。

1.1.7 proxy 代理

一個類被AOP織入增強後,就產生一個結果代理類

1.1.8 aspect 切面

是切入點和通知(引介)的結合

1.2 學習spring中的AOP要明確的事

  1. 開發階段(我們做的)
    (1)編寫核心業務代碼(開發主線):大部分程序員來做,要求熟悉業務需求。
    (2)把公用代碼抽取出來,製作成通知。(開發階段最後再做):AOP編程人員來做。
    (3)在配置文件中,聲明切入點與通知間的關係,即切面。:AOP編程人員來做。
  2. 運行階段(Spring框架完成的)
    Spring框架監控切入點方法的執行。一旦監控到切入點方法被運行,使用代理機制,動態創建目標對象的代理對象,根據通知類別,在代理對象的對應位置,將通知對應的功能織入,完成完整的代碼邏輯運行。

1.3 代理的選擇

在spring中,框架會根據目標類是否實現了接口來決定採用哪種動態代理的方式。

2 基於 xml 的AOP配置

2.1 pom

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <spring-version>5.0.2.RELEASE</spring-version>
  </properties>

  <dependencies>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring-version}</version>
    </dependency>

	 <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.13</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.16</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>

    <dependency>
      <groupId>commons-dbutils</groupId>
      <artifactId>commons-dbutils</artifactId>
      <version>1.6</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.1_3</version>
    </dependency>

2.2 Service

public interface IAccountService {
    /**
     * 模擬保存賬戶
     *
     */
    void saveAccount();
    void updateAccount(int i);
    int deleteAccount();
}
public class AccountServiceImpl implements IAccountService {


    @Override
    public void saveAccount() {
        System.out.println("執行了保存");
    }

    @Override
    public void updateAccount(int i) {
        System.out.println("執行了更新 " + i);
    }

    @Override
    public int deleteAccount() {
        System.out.println("執行了刪除");
        return 0;
    }
}

2.3 日誌記錄類

package com.tzb.myutils;

/**
 * 用於記錄日誌的工具類
 */
public class Logger {

    /**
     * 打印日誌,計劃讓其在切入點方法之前執行
     * 切入點方法就是業務層方法
     */
    public void printLog(){
        System.out.println("Logger類中的 printLog 開始記錄日誌。。。");
    }
}

2.5 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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->

    <!--配置 spring 的 IOC-->
    <bean id="accountService" class="com.tzb.service.impl.AccountServiceImpl"></bean>

    <!--AOP 配置步驟
        1、把通知 Bean 也交給 spring 管理
        2、使用 aop:config 標籤表明開始 AOP 的配置
        3、使用 aop:aspect 標籤表明開始配置切面
                id屬性:是給切面提供一個唯一標識
                ref屬性:指定通知類 bean 的id

        4、在 aop:aspect 標籤的內部使用對應的標籤來配置通知的類型
                現在的案例是讓 printlog 方法在切入點方法執行之前執行,所以是前置通知
                aop:brfore 配置前置通知
                    method屬性,指定 Logger 類中哪個方法是前置通知
                    pointcut屬性:指定切入點表達式,指對業務類中哪個方法增強

           切入點表達式的寫法:
                關鍵字: execution
                表達式:
                    訪問修飾符   返回值 包名.包名...類名.方法名(參數列表)

    -->

    <!--配置 Logger 類-->
    <bean id="logger" class="com.tzb.myutils.Logger"></bean>

    <!-- 配置 AOP-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect id="logAdvice" ref="logger">
            <!--配置通知類型,且建立通知方法和切入點的關聯-->
            <aop:before method="printLog" pointcut="execution(public void com.tzb.service.impl.AccountServiceImpl.saveAccount())"></aop:before>
        </aop:aspect>
    </aop:config>


</beans>

2.6 單元測試

public class AOPTest {
    public static void main(String[] args) {
        // 1.獲取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:bean.xml");

        // 2. 獲取對象
        IAccountService as = (IAccountService) ac.getBean("accountService");

        // 3.執行方法
        as.saveAccount();
    }
}

在這裏插入圖片描述

3 切入點表達式寫法

public class AOPTest {
    public static void main(String[] args) {
        // 1.獲取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:bean.xml");

        // 2. 獲取對象
        IAccountService as = (IAccountService) ac.getBean("accountService");

        // 3.執行方法
        as.saveAccount();

        as.updateAccount(1);

        as.deleteAccount();
    }
}

3.1 全通配寫法

* *..*.*(..)
在這裏插入圖片描述

3.2 訪問修飾符可以省略

void com.tzb.service.impl.AccountServiceImpl.saveAccount()
在這裏插入圖片描述

3.3 返回值使用通配符,表示任意返回值

* com.tzb.service.impl.AccountServiceImpl.saveAccount()
在這裏插入圖片描述

3.4 包名可以使用通配符,表示任意包

  • 但是有幾級包,就需要寫幾個 *.

3.5 包名可以使用..,表示當前包及其子包

* *..AccountServiceImpl.saveAccount()
在這裏插入圖片描述

3.6 類名和方法名都可以使用 *實現通配

* *..*.*()
在這裏插入圖片描述

3.7 參數列表

  • 可以直接寫參數類型:基本類型直接寫名稱;引用類型寫包名.類名
    * *..*.*(int)
    在這裏插入圖片描述

  • 可以使用通配符表示任意類型,但是必須有參數
    * *..*.*(*)
    在這裏插入圖片描述

  • 可以使用 ..,表示有參數或者無參數

3.8 實際開發中切入點表達式的通常寫法

切到業務層實現類下的所有方法
* com.tzb.service.impl.*.*(..)
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章