學習筆記-Spring Boot 源起-Spring AOP 程式設計
一、AOP面向方面程式設計
AOP是OOP的延續,是Aspect Oriented Programming的縮寫,意思是面向方面程式設計。OP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是呼叫者和被呼叫者之間的解耦,AOP可以說也是這種目標的一種實現。
AOP帶來了新的程式設計革新。 使得軟體設計更加細化,程式碼的編寫順序不再決定其執行順序,誕生動態元件概念,結合IOC DI模式,可以實現軟體編寫和執行的最大靈活性,自誕生以來已經成為Java領域主流模式,典型框架有Spring或AspectJ框架。
在面向物件系統中,我們經常需要一些任務活動,如記錄,交易的安全性,快取等,這些活動是必要的,但不是業務邏輯的一部分,被稱為"橫切關注點"。
(橫切關注==跨整個系統的常用功能)
從業務邏輯中分離橫切關注點,為寫出一個精心設計的解耦系統邁出了一大步。讓我們關注思考如何處理橫切關注點的分離。
1、繼承Inheritance
繼承的是那些通用功能,繼承需要我們設計一個基類,如果我們要重用到多個地方可能需要修改這個基類。繼承Inheritance == 難以在後來修改(無彈性程式碼)
2、委託Delegation
委託是一個處理橫切關注的好辦法,委託和組合分享一個通用功能,但是我們需要在很多地方呼叫這個委託物件,顯得繁瑣。委託 == 繁瑣
AOP允許我們以模組化關注橫向點並形成物件,稱為Aspect,這樣使用Aspect能夠建立一個乾淨解耦的程式碼。
二、AOP相關概念
1、Concerns關注 – 這是基於功能的模組部分,有兩種型別關注:. 1. 核心關注 2. 跨切面關注(Aspect). 核心關注是有關業務邏輯,比如生成工資單,讓員工記錄,做銀行轉帳。跨切面關注是配合業務的一些活動任務,如日誌 快取等。
2、Joinpoint連線點 – Joinpoint是在執行時的切入點,Aspect也就是跨切面關注的一些功能要加入主要業務功能的地方,一個連線點可以是一個被呼叫的方法。
3、Advice建議 – 每個Aspect都有一個目標,它的任務是什麼,這個任務會被切入到哪個連線點,這些都被稱為Advice. Advice能夠定義Aspect什麼時候執行任務,是在核心關注也就是主要業務活動的之前 之後或者前後執行?
4、Pointcut 切入點– 一個系統有很多連線點,但是並不是所有連線點都需要被選擇切入Aspect的,Aspect從切入點能夠獲得幫助,選擇哪個連線點介入。
5、Aspect方面 – Advice 和 Pointcut定義了一個方面Aspect.Advice定義了Aspect的任務和什麼時候執行它,而切入點Pointcut定義在哪裡具體地方切入,也就是說,Aspect定義了它是什麼東西 什麼時候切入和在哪裡切入。
6、Target目標 – 目標是一個被切入的地方,它一般是核心關注,業務功能。
7、Proxy代理 – 當一個advice應用到目標物件時,這時一個代理物件將被建立. AOP容器建立和管理代理物件的生命週期。
8、Weaving織入 – Weaving是一個混合橫向方面到目標業務物件的過程,織入可以是在編譯時間,也可以在執行時間使用classload,Spring AOP預設是在執行時間。
三、Spring AOP 簡單樣例
Spring 支援 Aspect J 的註解式方面程式設計。
下面通過一個例子展示Spring 的 AOP 的使用。
1、新建maven專案,編輯pom.xml 使之支援Spring AOP
<!-- 新增spring framework 依賴 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- spring aop 支援 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- aspectj 支援 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
2、編寫攔截規則
/**
*
*/
package com.freesky.spring_aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 攔截規則的註解
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String name();
}
3、編寫使用註解的被攔截類
package com.freesky.spring_aop;
import org.springframework.stereotype.Service;
/**
*
* 使用註解的被攔截類
*
*/
@Service
public class DemoUseAnnotationService {
@Action(name="註解式攔截的add操作")
public void add() {}
}
4、編寫使用方法的被攔截類
package com.freesky.spring_aop;
import org.springframework.stereotype.Service;
/**
*
* 使用方法規則被攔截類
*
*/
@Service
public class DemoUseMethodService {
public void add() {}
}
5、編寫切面
package com.freesky.spring_aop;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
/**
*
* 切面類
*
*/
@Aspect // 這裡宣告這是一個切面
@Component // 這裡讓此切面成為Spring容器的管理的一個Bean
public class MyAspect {
// 此處宣告切點
@Pointcut("@annotation(com.freesky.spring_aop.Action)")
public void annotationPointcut() {};
// 這裡宣告一個建言,並使用 @PointCut 定義的切點
@After("annotationPointcut()")
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
// 通過反射獲取註解上的屬性,然後做相關的操作
System.out.println("註解式攔截" + action.name());
}
// 宣告一個建言,此建言直接使用攔截規則作為引數
@Before("execution(* com.freesky.spring_aop.DemoUseMethodService.*(..))")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法規則式攔截" + method.getName());
}
}
6、編寫配置類:
package com.freesky.spring_aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.freesky.spring_aop")
@EnableAspectJAutoProxy // 這裡開啟 Spring 對 AspectJ 代理的支援
public class AopConfig {
}
7、執行測試程式碼
package com.freesky.spring_aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Spring AOP 測試程式
*
*/
public class App
{
public static void main( String[] args )
{
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
DemoUseAnnotationService demoUseAnnotationService = context.getBean(DemoUseAnnotationService.class);
DemoUseMethodService demoUseMethodService = context.getBean(DemoUseMethodService.class);
demoUseAnnotationService.add();
demoUseMethodService.add();
context.close();
}
}
四、結束語
通過樣例可以很明顯的看出面向切面(方面)的優勢,相比與OOP,在攔截日誌方面優勢明顯。
關於AOP,更加深入的內容需要繼續學習研究,深刻理解內部原理,就能舉一反三了。
參考:
https://www.jdon.com/aop.html
《JavaEE開發的顛覆者 Spring Boot實戰》