本文原文地址:什麼是AOP面向切面編程?怎麼簡單理解?
什麼是AOP面向切面編程
面向切面編程(AOP)通過將橫切關注點(cross-cutting concerns)分離出來,提供了一種增強代碼模塊化和可維護性的方法。
簡單來説,AOP就是將公共的模塊封裝成公共的方法,然後在需要的時候(這個就是切入點),直接就可以調用,而不用在各個對象裏面具體的實現。
AOP是一種新的編程方式,它和OOP不同,OOP把系統看作多個對象的交互,AOP把系統分解為不同的關注點,或者稱之為切面(Aspect)。這個可以理解為把系統理解為一個流程,一個對象負責流程上的一個節點。
當然,AOP和公共模塊抽取調用的方式的差別在於切入點的調用方式的不同。AOP是通過某種方式(下面AOP原理會解釋)自動的調用,而不管是抽取公共方法,還是通過Proxy模式實現調用,都需要在每個業務方法上重複編寫調用。
以AOP的視角來編寫上述業務,可以依次實現:
- 核心邏輯,即BookService;
-
切面邏輯,即:
- 權限檢查的Aspect;
- 日誌的Aspect;
- 事務的Aspect。
然後,以某種方式,讓框架來把上述3個Aspect以Proxy的方式“織入”到BookService中,這樣一來,就不必編寫複雜而冗長的Proxy模式或者公共方法調用。
AOP原理
如何把切面織入到核心邏輯中?這正是AOP需要解決的問題。換句話説,如果客户端獲得了BookService的引用,當調用bookService.createBook()時,如何對調用方法進行攔截,並在攔截前後進行安全檢查、日誌、事務等處理,就相當於完成了所有業務功能。
在Java平台上,對於AOP的織入,有3種方式:
- 編譯期:在編譯時,由編譯器把切面調用編譯進字節碼,這種方式需要定義新的關鍵字並擴展編譯器,AspectJ就擴展了Java編譯器,使用關鍵字aspect來實現織入;
- 類加載器:在目標類被裝載到JVM時,通過一個特殊的類加載器,對目標類的字節碼重新“增強”;
- 運行期:目標對象和切面都是普通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 的簡單示例,展示瞭如何定義和應用切面。
-
定義切面
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 包中的所有方法執行之前運行。
-
配置 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> -
使用目標對象
package com.example.service; public class UserService { public void createUser() { System.out.println("Creating user"); } } -
測試 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 通過將橫切關注點分離出來,提供了一種增強代碼模塊化和可維護性的方法。通過定義切面、連接點、切入點和通知,可以在不修改現有代碼的情況下,動態地將橫切關注點織入到程序中。