更新時間:2022-06-13 09:33:48 來源:動力節點 瀏覽1384次
在本文中,我們將使用 Spring 中的 AOP 支持來實現自定義 AOP 注釋。
首先,我們將給出 AOP 的高級概述,解釋它是什么以及它的優點。在此之后,我們將逐步實現我們的注解,逐步建立對 AOP 概念的更深入理解。
結果將是對 AOP 的更好理解以及未來創建自定義 Spring 注釋的能力。
快速總結一下,AOP 代表面向方面的編程。從本質上講,它是一種無需修改代碼即可向現有代碼添加行為的方法。
關于 AOP 的詳細介紹,有關于 AOP切入點和建議的文章。本文假設我們已經具備基本知識。
我們將在本文中實現的 AOP 類型是注解驅動的。如果我們使用過 Spring @Transactional注解,我們可能已經對此很熟悉了:
@Transactional
public void orderGoods(Order order) {
// A series of database calls to be performed in a transaction
}
這里的關鍵是非侵入性。通過使用注釋元數據,我們的核心業務邏輯不會被我們的事務代碼污染。這使得推理、重構和隔離測試變得更容易。
有時,開發 Spring 應用程序的人可以將其視為“ Spring Magic”,而無需詳細考慮它是如何工作的。實際上,發生的事情并不是特別復雜。但是,一旦我們完成了本文中的步驟,我們將能夠創建自己的自定義注解,以便理解和利用 AOP。
首先,讓我們添加我們的Maven依賴機制。
對于這個例子,我們將使用 Spring Boot,因為它的約定優于配置的方法讓我們能夠盡快啟動并運行:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
請注意,我們已經包含了 AOP 啟動器,它引入了我們開始實現方面所需的庫。
我們要創建的注釋將用于記錄方法執行所需的時間。讓我們創建我們的注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
盡管實現相對簡單,但值得注意的是這兩個元注釋的用途。
@Target注解告訴我們注解適用的地方。 這里我們使用的是ElementType.Method,這意味著它只適用于方法。如果我們嘗試在其他任何地方使用注解,那么我們的代碼將無法編譯。這種行為是有道理的,因為我們的注釋將用于記錄方法執行時間。
@Retention只是說明注解在運行時是否對 JVM 可用。默認情況下它不是,所以 Spring AOP 將無法看到注解。這就是它被重新配置的原因。
現在我們有了注釋,讓我們創建我們的方面。這只是將封裝我們的橫切關注點的模塊,在我們的例子中是方法執行時間日志記錄。它只是一個類,用@Aspect注釋:
@Aspect
@Component
public class ExampleAspect {
}
我們還包含了@Component 注釋,因為我們的類也需要是一個 Spring bean 才能被檢測到。本質上,這是我們將實現我們希望自定義注解注入的邏輯的類。
現在,讓我們創建我們的切入點和建議。這將是一個存在于我們方面的帶注釋的方法:
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
return joinPoint.proceed();
}
從技術上講,這并沒有改變任何東西的行為,但是仍然有很多事情需要分析。
首先,我們用@Around注釋了我們的方法。這是我們的建議,圍繞建議意味著我們在方法執行之前和之后添加額外的代碼。還有其他類型的建議,例如之前和之后,但它們將超出本文的范圍。
接下來,我們的@Around注釋有一個切入點參數。我們的切入點只是說,“將此建議應用于任何帶有@LogExecutionTime注釋的方法。” 還有很多其他類型的切入點,但如果作用域,它們將再次被排除在外。
logExecutionTime()方法本身就是我們的建議。有一個參數ProceedingJoinPoint。在我們的例子中,這將是一個使用@LogExecutionTime 注釋的執行方法。
最后,當我們的注解方法最終被調用時,會發生的是我們的通知將首先被調用。然后由我們的建議決定下一步該做什么。在我們的例子中,我們的建議除了調用proceed()之外什么都不做,它只是調用原始的帶注釋的方法。
現在我們已經有了我們的骨架,我們需要做的就是在我們的建議中添加一些額外的邏輯。除了調用原始方法之外,這將是記錄執行時間的內容。讓我們將這個額外的行為添加到我們的建議中:
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
同樣,我們在這里沒有做任何特別復雜的事情。我們剛剛記錄了當前時間,執行了方法,然后將花費的時間打印到控制臺。我們還記錄了方法簽名,它是為使用連接點實例而提供的。如果我們愿意,我們還可以訪問其他信息,例如方法參數。
現在,讓我們嘗試用@LogExecutionTime 注釋一個方法,然后執行它來看看會發生什么。請注意,這必須是 Spring Bean 才能正常工作:
@LogExecutionTime
public void serve() throws InterruptedException {
Thread.sleep(2000);
}
執行后,我們應該會看到控制臺記錄了以下內容:
void org.baeldung.Service.serve() executed in 2030ms
以上就是關于“實現自定義Spring AOP注解”介紹,大家如果對此比較感興趣,想了解更多相關知識,不妨來關注一下動力節點的Spring教程,里面的課程內容由淺到深,細致全面,很適合沒有基礎的小伙伴學習,希望對大家能夠有所幫助哦。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習