Spring AOP與AspectJ的對比及應用

来源:https://www.cnblogs.com/larrydpk/archive/2023/02/07/17099706.html
-Advertisement-
Play Games

1 簡介 AOP,即面向切麵編程是很常用的技術,特別是在Java Web開發中。而最流行的AOP框架分別是Spring AOP和AspectJ。 2 Spring AOP vs AspectJ Spring AOP是基於Spring IoC實現的,它解決大部分常見的需求,但它並不是一個完整的AOP解 ...


1 簡介

AOP,即面向切麵編程是很常用的技術,特別是在Java Web開發中。而最流行的AOP框架分別是Spring AOP和AspectJ。

2 Spring AOP vs AspectJ

Spring AOP是基於Spring IoC實現的,它解決大部分常見的需求,但它並不是一個完整的AOP解決方案。對於非Spring容器管理的對象,它更沒有辦法了。而AspectJ旨在提供完整的AOP方案,因此也會更複雜。

2.1 織入方式

兩者織入方式有極大的不同,這也是它們的本質區別,它們實現代理的方式不同。

AspectJ是在運行前織入的,分為三類:

  • 編譯時織入
  • 編譯後織入
  • 載入時織入

因此需要AspectJ編譯器(ajc)的支持。

而Spring AOP是運行時織入的,主要使用了兩種技術:JDK動態代理和CGLIB代理。對於介面使用JDK Proxy,而繼承的使用CGLIB。

spring-aop-vs-aspectj.spring-aop

2.2 Joinpoints

因為織入方式的區別,兩者所支持的Joinpoint也是不同的。像final的方法和靜態方法,無法通過動態代理來改變,所以Spring AOP無法支持。但AspectJ是直接在運行前織入實際的代碼,所以功能會強大很多。

Joinpoint Spring AOP Supported AspectJ Supported
Method Call No Yes
Method Execution Yes Yes
Constructor Call No Yes
Constructor Execution No Yes
Static initializer execution No Yes
Object initialization No Yes
Field reference No Yes
Field assignment No Yes
Handler execution No Yes
Advice execution No Yes

2.3 性能

編譯織入會比較運行時織入快很多,Spring AOP是使用代理模式在運行時才創建對應的代理類,效率沒有AspectJ高。

3 Spring Boot使用AspectJ

因為AspectJ比較強大,在項目中應用會更多,所以這裡只介紹它與Spring Boot的集成。

3.1 引入依賴

引入以下依賴,在Spring Boot基礎上加了Lombok和aspectj:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${aspectj.version}</version>
  </dependency>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${aspectj.version}</version>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
    <scope>provided</scope>
  </dependency>
</dependencies>

3.2 被AOP的對象

為了驗證AOP的功能,我們添加一個TestController,它有一個處理Get請求的方法,同時會調用private的成員方法和靜態方法:

@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
    @GetMapping("/hello")
    public String hello() {
        log.info("------hello() start---");
        test();
        staticTest();
        log.info("------hello() end---");
        return "Hello, pkslow.";
    }

    private void test() {
        log.info("------test() start---");
        log.info("test");
        log.info("------test() end---");
    }

    private static void staticTest() {
        log.info("------staticTest() start---");
        log.info("staticTest");
        log.info("------staticTest() end---");
    }
}

3.3 配置Aspect

配置切麵如下:

@Aspect
@Component
@Slf4j
//@EnableAspectJAutoProxy
public class ControllerAspect {

    @Pointcut("execution(* com.pkslow.springboot.controller..*.*(..))")
    private void testControllerPointcut() {

    }

    @Before("testControllerPointcut()")
    public void doBefore(JoinPoint joinPoint){
        log.info("------pkslow aop doBefore start------");
        String method = joinPoint.getSignature().getName();
        String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
        log.info("Method: {}.{}" ,declaringTypeName, method);
        log.info("------pkslow aop doBefore end------");
    }

    @Around("testControllerPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("------pkslow aop doAround start------");
        long start = System.nanoTime();
        Object obj = joinPoint.proceed();
        long end = System.nanoTime();
        log.info("Execution Time: " + (end - start) + " ns");
        log.info("------pkslow aop doAround end------");

        return obj;
    }
}

@Pointcut定義哪些類和方法會被捕抓來代理,這裡配置的是controller下的所有方法。

@Before@Around則定義了一些處理邏輯。@Before是列印了方法名,而@Around是做了一個計時。

註意:是不需要配置@EnableAspectJAutoProxy的。

3.4 maven插件

因為是需要編譯時織入代碼,所以需要maven插件的支持:https://github.com/mojohaus/aspectj-maven-plugin

配置好pom.xml文件即可。

然後執行命令打包:

mvn clean package

這時會顯示一些織入信息,大致如下:

[INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))

看到以上信息,說明成功織入了代碼,具體可以查看生成的class文件。

可以看到有許多代碼都不是我們寫的,而是織入生成。

3.5 執行及測試

編譯成功後,我們就執行代碼。如果是通過IDEA來執行,則在運行前不需要再build了,因為已經通過maven build過了包。通過IDEA自帶的編譯器build,可能無法織入。或者選擇ajc作為編譯器。具體請參考:IDEA啟動Springboot但AOP失效

訪問如下:

GET http://localhost:8080/test/hello

則日誌如下,成功實現AOP功能:

3.6 一些遇到的錯誤

遇到錯誤:

ajc Syntax error, annotations are only available if source level is 1.5 or greater

需要配置插件:

<complianceLevel>${java.version}</complianceLevel>
<source>${java.version}</source>
<target>${java.version}</target>

可能還會遇到無法識別Lombok的錯誤,配置如下則解決該問題:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>aspectj-maven-plugin</artifactId>
  <version>1.14.0</version>
  <configuration>
    <complianceLevel>${java.version}</complianceLevel>
    <source>${java.version}</source>
    <target>${java.version}</target>
    <proc>none</proc>
    <showWeaveInfo>true</showWeaveInfo>
    <forceAjcCompile>true</forceAjcCompile>
    <sources/>
    <weaveDirectories>
      <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
    </weaveDirectories>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

4 總結

AOP場景應用特別多,還是需要掌握的。

代碼請看GitHub: https://github.com/LarryDpk/pkslow-samples


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 如果想實現chatGPT的網頁版,調用介面就可以了,但是如果需要聯繫上下文語境,就需要在傳遞的數據的時候進行下拼接 傳參的時候對所有的對話數據進行拼接,拼成下麵這樣 {"prompt":"(You:在嗎\n)這裡在哦,有什麼可以幫助你的嗎?(You:你這個系統多少錢\n)抱歉,您想知道什麼?這裡是客 ...
  • 摘要:jstat命令可以查看堆記憶體各部分的使用量,以及載入類的數量。 本文分享自華為雲社區《JVM之通過jstat命令進行查看堆記憶體使用情況》,作者:共飲一杯無 。 基本概念 jstat是JDK自帶的一個輕量級小工具。它位於java的bin目錄下,主要利用JVM內建的指令對Java應用程式的資源和性 ...
  • R2M分散式鎖原理可以理解為一條內容或者圖片+文字+鏈接的載體,常見的案例有鎖說明和分散式鎖選擇、r2m分散式鎖選擇、r2m分散式鎖原理,加鎖核心流程。 ...
  • 問題場景:當用戶量增大的時候,系統內的消息推送(比如 系統內簡訊推送,微信,釘釘,極光個推推送)將是我們常見業務場景。當系統中的用戶量逐漸增大時候,群發用戶消息的推送也將成為系統中致命的性能瓶頸。 消息推送緩衝隊列模型 意義: 構建大批量消息推送隊列,來解決大批量數據通過拉長時間隊列模式來換取性能提 ...
  • 一、認證組件 簡介: 登錄認證的限制 認證組件是drf框架給我們提供的認證介面,它能夠在請求進入視圖函數/類前進驗證(例如:認證用戶是否登錄),對不符合認證的請求進行攔截並返回校驗失敗的信息 (1)、登錄介面 # 認證是基於登錄的介面上面操作的 所以前戲編寫一個簡單的登錄介面 models.py c ...
  • 視圖和視圖解析器 1.基本介紹 在SpringMVC中的目標方法,最終返回的都是一個視圖(有各種視圖) 註意,這裡的視圖是一個類對象,不是一個頁面!! 返回的視圖都會由一個視圖解析器來處理(視圖解析器有很多種) 2.自定義視圖 2.1為什麼需要自定義視圖 在預設情況下,我們都是返回預設的視圖,然後返 ...
  • 這篇文章主要描述分散式技術中的選舉演算法,分散式選舉是為選出一個主節點,由它來協調和管理其他節點,保證集群有序運行和節點間數據的一致性。涉及到的選舉演算法包括Bully演算法、Raft演算法和ZAB演算法。 ...
  • 最近在寫一本Xilinx的FPGA方面的書,現將HLS部分內容在這裡分享給大家,希望大家喜歡,也歡迎批評指正。[原創www.cnblogs.com/helesheng] 在可編程邏輯器件被用於電子系統設計的前期,由於所含的邏輯資源較少,絕大部分情況下,它們被用於實現數據的傳輸和介面電路。工程師們習慣 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...