切面導向程式設計
(Aspect-oriented programming, AOP),又譯為面向方面程式設計、剖面導向程式設計,此設計最主要目的是實現關注點分離(Separation of concerns),希望將專案的橫切關注點與業務核心主體進行分離,以提高程式碼的模組化程度,使得我們可以直接將與核心業務功能關係較不相關的功能直接添加至程式中,同時又不會造成核心功能的程式可讀性複雜化,例如 Log 紀錄檔功能。
故 AOP 機制可以讓我們將一些非功能性配置與核心業務功能進行分離,非功能性配置有例如日誌紀錄、效能統計、安全控制、事務處理、異常處理等配置,此優點又可以讓我們更專注在業務邏輯上的開發,不會有非功能性配置與業務功能耦合性問題。
AOP 有以下相關主要術語:
切入點(PointCut)
與 通知(Advice)
組成,主要就是用來設定切入點(PointCut)與切入特定動作(Advice)AOP
切入的位置,例如某個類別或函數PointCut
切入後的實際切入點
,通常是一個函數Joint Point
切入點實際要執行的動作,通常會將 Advice 模擬為一個攔截器(Interceptor),並且會在連接點(Join Point)上維護多個 Advice 進行層層攔截Advice 又可以分為五種類型:
五種類型執行順序為 @Around > @Before > @Around > @After > @AfterReturning
接下來我們直接介紹實作:
使用前面專案的 Todo 專案增加 AOP 方法,當使用者操作 Service 時,新增 Log 紀錄檔
新增 ServiceAspect.kt 檔案,內容如下:
@Aspect
@Component
class ServiceAspect {
// 第一個 * 表示任意返回值
// com.inroman.demo... 為 package 路徑
// 第二個 * 表示任何 Service 物件
// 第三個 .*(..) 則表示任何方法
@Pointcut("execution(* com.ironman.demo.service.*.*(..))")
fun pointcut() {}
// 設定 Before 通知並執行 pointcut 切點
@Before("pointcut()")
fun before(joinPoint: JoinPoint) {
// 設定 Logger 帶入切入點類別名稱
val logger = LoggerFactory.getLogger(joinPoint.target.javaClass.name)
// 取得切入點方法
val methodSignature: MethodSignature = joinPoint.signature as MethodSignature
// 取得切入點方法名稱
val methodName = methodSignature.method.name
// 取得切入點方法類別
val className = joinPoint.target.javaClass.name
// 取得切入點方法參數
val argsInfo = joinPoint.args
logger.info("[處理開始] Service: $className, Method:$methodName, Args: $argsInfo")
}
// 設定 After 通知並執行 pointcut 切點
@After("pointcut()")
fun after(joinPoint: JoinPoint) {
val logger = LoggerFactory.getLogger(joinPoint.target.javaClass.name)
val methodSignature: MethodSignature = joinPoint.signature as MethodSignature
val methodName = methodSignature.method.name
val className = joinPoint.target.javaClass.name
val argsInfo = joinPoint.args
logger.info("[處理結束] Service: $className, Method: $methodName, Args: $argsInfo")
}
}
觀察專案運行 Log