1. 程式人生 > 其它 >Spring AOP 01_初始AOP 及兩種動態代理方法

Spring AOP 01_初始AOP 及兩種動態代理方法

一、Spring AOP簡介

AOP的全稱是Aspect-Oriented Programming,即面向切面程式設計(也稱面向方面程式設計)。它是面向物件程式設計(OOP)的一種補充,目前已成為一種比較成熟的程式設計方式。

在傳統的業務處理程式碼中,通常都會進行事務處理、日誌記錄等操作。雖然使用OOP可以通過組合或者繼承的方式來達到程式碼的重用,但如果要實現某個功能(如日誌記錄),同樣的程式碼仍然會分散到各個方法中。這樣,如果想要關閉某個功能,或者對其進行修改,就必須要修改所有的相關方法。這不但增加了開發人員的工作量,而且提高了程式碼的出錯率。

為了解決這一問題,AOP思想隨之產生。AOP採取橫向抽取機制,將分散在各個方法中的重複程式碼提取出來,然後在程式編譯或執行時,再將這些提取出來的程式碼應用到需要執行的地方。這種採用橫向抽取機制的方式,採用傳統的OOP思想顯然是無法辦到的,因為OOP只能實現父子關係的縱向的重用。雖然AOP是一種新的程式設計思想,但卻不是OOP的替代品,它只是OOP的延伸和補充。

類與切面的關係


AOP的使用,使開發人員在編寫業務邏輯時可以專心於核心業務,而不用過多的關注於其他業務邏輯的實現,這不但提高了開發效率,而且增強了程式碼的可維護性。

AOP術語

Proxy(代理):將通知應用到目標物件之後,被動態建立的物件。
Weaving(織入):將切面程式碼插入到目標物件上,從而生成代理物件的過程。

二、動態代理

  1. JDK動態代理
    JDK動態代理是通過java.lang.reflect.Proxy 類來實現的,我們可以呼叫Proxy類的newProxyInstance()方法來建立代理物件。對於使用業務介面的類,Spring預設會使用JDK動態代理來實現AOP。

案例

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>org.example</groupId>
    <artifactId>Spring_AOP</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.5.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.3</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

一、靜態代理

1. 建構函式方法給代理物件賦值
介面類UserDao
public interface UserDao {
    public void addUser();
    public void deleteUser();
}
實現類UserDaoImpl
import sse.dao.UserDao;

public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("add user!");
    }

    @Override
    public void deleteUser() {
        System.out.println("delete user!");
    }
}
代理類UserDaoProxy
import sse.dao.UserDao;
import sse.dao.impl.UserDaoImpl;

//模擬日誌
public class UserDaoProxy implements UserDao {

    private  UserDao target;

    public UserDaoProxy(UserDao target) {  //建構函式方法給代理物件賦值
        this.target = target;
    }

    @Override
    public void addUser() {
        System.out.println("開始新增使用者");
        target.addUser();
        System.out.println("完成新增使用者");
    }

    @Override
    public void deleteUser() {
        System.out.println("開始刪除使用者");
        target.deleteUser();
        System.out.println("完成刪除使用者");
    }
}
測試
2. set方法
介面類BookDao
public interface BookDao {
    void addBook();
}
實現介面的類BookDaoImpl
import sse.dao.BookDao;

public class BookDaoImpl implements BookDao {
    @Override
    public void addBook() {
        System.out.println("新增書籍");
    }
}
靜態代理BookDaoProxy
import sse.dao.BookDao;

public class BookDaoProxy implements BookDao {

    private BookDao target;

    public void setTarget(BookDao target) { //set方法給代理物件賦值
        this.target = target;
    }

    @Override
    public void addBook() {
        System.out.println("開始新增書籍");
        target.addBook();
        System.out.println("完成新增書籍");
    }
}
測試

二、jdk動態代理

動態代理類JdkProxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//通用代理類
public class JdkProxy implements InvocationHandler {
    private  Object target;//通用代理類,既可以代理UserDao,也可以代理BookDao

    public JdkProxy(Object target) {
        this.target = target;
    }

    /*
*   invoke方法在反射機制中用到,它可以根據方法名動態地呼叫不同的方法,有可能呼叫addUser(),也有可能呼叫addBook(),我們把它們組合到invoke()方法裡面
*    在invoke方法裡我們通過引數呼叫實際的方法(具體的方法是通過引數傳進來的)
*    通過method.invoke()傳進來的有可能是addUser(),也可能是addBook(),方法裡面有兩個引數,一個是呼叫的target,一個是其引數
* * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println(method.getName()+"被呼叫前");
        Object ret = method.invoke(target,args);
        System.out.println(method.getName()+"被呼叫後");

        return ret;

    }
}
  • 測試1
  • 測試2