博客 / 詳情

返回

什麼是AOP面向切面編程?怎麼簡單理解?

本文原文地址:什麼是AOP面向切面編程?怎麼簡單理解?

什麼是AOP面向切面編程

面向切面編程(AOP)通過將橫切關注點(cross-cutting concerns)分離出來,提供了一種增強代碼模塊化和可維護性的方法。

簡單來説,AOP就是將公共的模塊封裝成公共的方法,然後在需要的時候(這個就是切入點),直接就可以調用,而不用在各個對象裏面具體的實現。

AOP是一種新的編程方式,它和OOP不同,OOP把系統看作多個對象的交互,AOP把系統分解為不同的關注點,或者稱之為切面(Aspect)。這個可以理解為把系統理解為一個流程,一個對象負責流程上的一個節點。

當然,AOP和公共模塊抽取調用的方式的差別在於切入點的調用方式的不同。AOP是通過某種方式(下面AOP原理會解釋)自動的調用,而不管是抽取公共方法,還是通過Proxy模式實現調用,都需要在每個業務方法上重複編寫調用。

以AOP的視角來編寫上述業務,可以依次實現:

  • 核心邏輯,即BookService;
  • 切面邏輯,即:

    1. 權限檢查的Aspect;
    2. 日誌的Aspect;
    3. 事務的Aspect。

然後,以某種方式,讓框架來把上述3個Aspect以Proxy的方式“織入”到BookService中,這樣一來,就不必編寫複雜而冗長的Proxy模式或者公共方法調用。

AOP原理

如何把切面織入到核心邏輯中?這正是AOP需要解決的問題。換句話説,如果客户端獲得了BookService的引用,當調用bookService.createBook()時,如何對調用方法進行攔截,並在攔截前後進行安全檢查、日誌、事務等處理,就相當於完成了所有業務功能。

在Java平台上,對於AOP的織入,有3種方式:

  1. 編譯期:在編譯時,由編譯器把切面調用編譯進字節碼,這種方式需要定義新的關鍵字並擴展編譯器,AspectJ就擴展了Java編譯器,使用關鍵字aspect來實現織入;
  2. 類加載器:在目標類被裝載到JVM時,通過一個特殊的類加載器,對目標類的字節碼重新“增強”;
  3. 運行期:目標對象和切面都是普通Java類,通過JVM的動態代理功能或者第三方庫實現運行期動態織入。

最簡單的方式是第三種,Spring的AOP實現就是基於JVM的動態代理。由於JVM的動態代理要求必須實現接口,如果一個普通類沒有業務接口,就需要通過CGLIB或者Javassist這些第三方庫實現。

AOP技術看上去比較神秘,但實際上,它本質就是一個動態代理,讓我們把一些常用功能如權限檢查、日誌、事務等,從每個業務方法中剝離出來。

需要特別指出的是,AOP對於解決特定問題,例如事務管理非常有用,這是因為分散在各處的事務代碼幾乎是完全相同的,並且它們需要的參數(JDBC的Connection)也是固定的。另一些特定問題,如日誌,就不那麼容易實現,因為日誌雖然簡單,但打印日誌的時候,經常需要捕獲局部變量,如果使用AOP實現日誌,我們只能輸出固定格式的日誌,因此,使用AOP時,必須適合特定的場景。

核心概念

  • 切面(Aspect):切面是封裝橫切關注點的模塊。它定義了在何處以及如何應用這些關注點。
  • 連接點(Join Point):連接點是程序執行過程中可以插入切面的點。例如,方法調用、方法執行、構造函數調用、字段訪問等。
  • 切入點(Pointcut):切入點定義了在哪些連接點上應用切面。它通常使用表達式來匹配特定的連接點。
  • 通知(Advice):通知是在特定的切入點上執行的代碼。通知可以在方法執行之前、之後或異常拋出時執行。常見的通知類型包括:

    • 前置通知(Before):在方法執行之前執行。
    • 後置通知(After):在方法執行之後執行。
    • 返回通知(After Returning):在方法成功返回之後執行。
    • 異常通知(After Throwing):在方法拋出異常之後執行。
    • 環繞通知(Around):包圍方法的執行,可以在方法執行之前和之後自定義行為。
  • 織入(Weaving):織入是將切面應用到目標對象的過程。織入可以在編譯時、類加載時或運行時進行。

示例

以下是一個使用 Spring AOP 的簡單示例,展示瞭如何定義和應用切面。

  1. 定義切面

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class LoggingAspect {
    
     @Before("execution(* com.example.service.*.*(..))")
     public void logBeforeMethod() {
         System.out.println("Method is about to be executed");
     }
    }

    在這個示例中,LoggingAspect 是一個切面,它包含一個前置通知 logBeforeMethod。這個通知將在 com.example.service 包中的所有方法執行之前運行。

  2. 配置 Spring AOP
    在 Spring 配置文件中啓用 AOP 支持,並註冊切面:

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/aop
                            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
     <aop:aspectj-autoproxy/>
    
     <bean id="loggingAspect" class="com.example.aspect.LoggingAspect"/>
    </beans>
  3. 使用目標對象

    package com.example.service;
    
    public class UserService {
     public void createUser() {
         System.out.println("Creating user");
     }
    }
  4. 測試 AOP

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import com.example.service.UserService;
    
    public class Main {
     public static void main(String[] args) {
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         UserService userService = (UserService) context.getBean("userService");
         userService.createUser();
     }
    }

    運行這個示例時,輸出將會是:

    Method is about to be executed
    Creating user

    這表明前置通知在 createUser 方法執行之前被調用了。

AOP 通過將橫切關注點分離出來,提供了一種增強代碼模塊化和可維護性的方法。通過定義切面、連接點、切入點和通知,可以在不修改現有代碼的情況下,動態地將橫切關注點織入到程序中。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.