Aop面向切面實現開發日志收集打印一文輕松搞定,內附詳細圖文示例+源碼自取

目錄

介紹

動態代理

jdk動態代理

cglib動態代理

注解實現Aop?

添加必須依賴

添加Atm類 (主業務邏輯代碼塊)

?定義打印log方法(提取公共代碼邏輯塊)

啟用代理?

切點表達式

Aop通知類型?

前置通知(@Before)

后置通知(@After)

正常結束通知(@AfterReturning)

異常結束通知(@AfterThrowing)

環繞通知

?切面的優先級

?Aop使用注意點

方法權限不能是private

其他方法在內部被調用時不會被增強

注解實現Aop

基于注解實現服務層打印入參和返回參數日志

未使用Aop效果

使用注解實現Aop入參返回值等日志打印

示例源碼


介紹

AOP(Aspect-Oriented Programming,面向切面編程)是一種編程范式,可以在不改變原有代碼的情況下,通過在程序的各個關鍵點上增加切面(Aspect)的方式,實現對代碼的增強和橫切關注點的分離,從而提高代碼的可重用性、可維護性和可擴展性。

AOP的核心思想是將程序中的不同關注點進行解耦,避免不同的關注點相互嵌入,導致代碼冗長、難以維護的問題。通過將不同的關注點抽象成切面,實現了程序的層次性,使得不同層次中的各種關注點可以獨立地進行開發、管理和維護。

在實現AOP時,需要定義切點(Pointcut)和切面(Aspect)兩個概念。切點用于定義程序中需要增強的關鍵點,例如方法的調用、異常拋出、對象的初始化等等。切面則是對切點進行增強的具體實現,例如日志記錄、性能監測、事務管理等等。AOP框架通過在程序中動態生成代理對象的方式,將切面織入到切點上,從而實現對程序的增強。

在Java中,常用的AOP框架包括Spring AOP和AspectJ。Spring AOP是基于代理的AOP框架,可以通過配置文件或注解的方式實現切面的定義;而AspectJ是基于注解的AOP框架,可以直接在Java代碼中使用注解的方式定義切點和切面。

動態代理

動態代理是一種用于實現面向切面編程的技術。它允許您編寫一些代碼來控制某個類或接口的行為。它可以在不更改原始代碼的情況下改變或增強類的行為。當類的對象被創建時,動態代理會在內存中創建一個新的類和一個代理類,以控制原始類的行為。

下面看一個代碼示例:

?測試

可以看到這里兩個方法除了各自方法的核心業務邏輯外,方法執行前和執行后是重復的非核心業務代碼,這里方法只有兩個,如果方法成百上千時就需要改很多的地方,從而可以使用動態代理進行優化處理

jdk動態代理

JDK動態代理是通過Java自帶的反射機制實現的,主要涉及兩個類:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

Proxy類是JDK提供的動態代理類,它提供了用于創建動態代理對象的靜態方法newProxyInstance()。這個方法有三個參數:

  1. ClassLoader對象,用于指定動態代理類的ClassLoader,一般使用被代理對象的ClassLoader
  2. Class<?>[]對象數組,用于指定被代理類實現的接口;
  3. InvocationHandler對象,用于指定動態代理對象的方法調用處理器。

InvocationHandler接口是一個函數式接口,它只有一個invoke()方法,用于處理動態代理類的方法調用。invoke()方法有三個參數:

  1. Object對象,表示被代理對象;
  2. Method對象,表示方法對象;
  3. Object[]數組,表示方法參數。

當動態代理類的方法被調用時,JVM會自動調用代理對象的invoke()方法,將方法名、方法參數等作為參數傳入該方法。在invoke()方法中,我們可以通過反射機制執行被代理類中的相應方法,并對方法的返回值進行處理和返回。

總體來說,JDK動態代理是在運行時動態生成一個類,并在該類中實現代理接口中的方法。當該類的方法被調用時,JVM會調用invoke()方法,從而實現代理方法的處理和返回。

執行測試:

?這樣的話后續再新增新的業務方法時只需要進行接口方法的核心業務的書寫,而不需要再關注非核心業務代碼的處理,非核心業務的代碼統一在代理對象中處理即可

cglib動態代理

CGLib 動態代理的實現原理主要是利用 ASM 字節碼操作庫和 Java 反射機制,在程序運行時動態地生成一個新類。這個新類繼承自被代理的類,并覆蓋掉所有非 final 方法,然后在這些方法中插入額外的操作(如日志記錄、權限驗證、事務控制等),達到動態代理的目的。 CGLib 通過子類化的方式來實現代理,所以它只能代理出接口的實現類,而不能直接代理接口。CGLib 使用 ASM 框架直接讀取 Class 文件,并對其進行轉換,從而生成所需的子類,生成的代理對象可以直接訪問到被代理類的所有屬性和方法。而且由于它是子類化的,所以即使是沒有接口的類也可以代理。因此,CGLib 動態代理的應用范圍更加廣泛。 由于 CGLib 實現的是通過字節碼技術產生的子類來實現代理行為,所以代理速度更快,但也存在一定的局限性,例如被代理類不能是 final 類型的、代理的目標方法不能是 final 的等。

下面用cglib來實現前面的示例

?執行測試:

注解實現Aop?

添加必須依賴

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version>
</dependency>

添加Atm類 (主業務邏輯代碼塊)

?定義打印log方法(提取公共代碼邏輯塊)

啟用代理?

測試:

?

切點表達式

Aop通知類型?

前置通知(@Before)

在連接點方法執行之前的增強處理

前面演示的demo就是使用的前置通知@before,這里不再演示

后置通知(@After)

在連接點方法執行之后的增強處理,無論正常結束還是異常結束,都會執行的處理

調整atm方法

添加后置執行方法

執行測試:

手動調整異常再進行測試

正常結束通知(@AfterReturning)

在連接點方法正常結束后會進行的處理? ?如果方法有返回值,可以拿到方法的返回值

添加正常結束通知

剛才前面的例子中我們傳入參數100時是手動定義的一個異常,那么這里添加一個正常結束通知不會執行才對,測試看下

?調整參數,不拋出異常觀察正常通知是否執行

獲取正常通知參數

?可以看到這里的方法是有返回值,可以通過正常通知來獲取該返回值,從而做進一步的業務處理

再執行測試

異常結束通知(@AfterThrowing)

在連接點方法異常結束后會進行的處理? 可以獲取異常的信息

添加異常結束通知方法

此時先執行一個不會拋異常的方法觀察異常結束通知會不會執行

可以看到正常結束通知執行了,異常結束通知沒有執行

調整參數為100的異常執行,查看異常通知方法的執行情況

?可以查看到異常結束通知執行,正常結束通知沒有執行

獲取異常信息

執行異常測試

?注意此處的異常信息只是捕獲到了,沒有做任何的處理

環繞通知

通過代碼調用方法,在方法的執行周圍進行增強

先注釋掉之前的通知方法

添加環繞通知

先進行異常測試

進行正常結束測試

?注意:

當有前置后置以及環繞通知時,先進行環繞通知,在方法的具體執行前后進行增強

放開所有通知方法進行測試

注意觀察通知執行的順序

?切面的優先級

當我們的程序中定義了多個切面時,可以通過@Order(數字)來控制各個切面的執行順序,其中數字越小,執行優先級越高

話不多說,直接上代碼

在之前原先的基礎上再定義一個切面類

日志信息也稍作調整以做區分,Order設為1,

原先的log切面Order設置為2

?啟動測試查看執行優先級

可以觀察到優先級順序

?Aop使用注意點

方法權限不能是private

連接點方法不能是private,會導致Aop不能進行增強

前面舉例時的take是public的aop可以進行正常增強,那么如果調整為private,再進行測試看看:

測試?

?Aop增強失效

如果是protected呢

protected也可以進行aop增強

其他方法在內部被調用時不會被增強

直接上代碼,這里直接調用兩個方法來進行增強

?此時兩個方法都被增強

如果在取錢方法中調用了存錢方法,觀察此時的增強通知會執行幾次

測試

?可以看到增強通知只執行了一次,且只執行了取錢方法的增強通知

注解實現Aop

基于注解實現服務層打印入參和返回參數日志

在業務開發中我們有時需要通過日志打印入參參數和方法返回,但是基本傳統寫法都是在方法中自定義log日志,這樣寫其實并不太優雅,可以通過aop進行優化日志打印

未使用Aop效果

新建兩個實體模擬存儲保存訂單Do和更新訂單Do

創建服務層模擬業務

?調用接口查看打印日志

可以看到只是執行了服務層的主業務核心代碼System.out.println模擬的業務代碼,如果還想打印傳參和返回值信息就需要使用日志打印log.info("日志信息")等

可以使用Aop進行業務優化

使用注解實現Aop入參返回值等日志打印

新建切面注解

?新建統一轉換參數

新建saveOrder和updateOrder轉換Operate

?

定義主要切面類

import cn.hutool.json.JSONUtil;
import com.example.demo23.demos.web.annotation.MyLogOperate;
import com.example.demo23.demos.web.service.Convert;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@Component
@Aspect
public class LogAsept {// 定義切入點@Pointcut("@annotation(com.example.demo23.demos.web.annotation.MyLogOperate)")public void pointcut(){}private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,1,1,TimeUnit.SECONDS,new LinkedBlockingDeque<>());//環繞通知@Around("pointcut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{Object obj = proceedingJoinPoint.proceed();threadPoolExecutor.execute(()->{try{MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();MyLogOperate myLogOperate = methodSignature.getMethod().getAnnotation(MyLogOperate.class);Class<? extends Convert> convert = (Class<? extends Convert>) myLogOperate.convert();Convert logConvert = convert.newInstance();OperateLogDo operateLogDo = logConvert.convert(proceedingJoinPoint.getArgs()[0]);operateLogDo.setDesc(myLogOperate.desc()).setResult(obj.toString());System.out.println("插入 operateLog" + JSONUtil.parseObj(operateLogDo));}catch (InstantiationException ex) {throw new RuntimeException(ex);} catch (IllegalAccessException ex) {throw new RuntimeException(ex);}});return obj;}
}

服務層添加注解使用aop進行日志切入

啟動測試:

切入成功,打印入參和方法返回值成功

?還可以再詳細一些打印出接口的調用時間,方法,路徑等

示例源碼

需要源碼的伙伴在下面自取即可

鏈接:aop示例源碼?
提取碼:z2wp?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/167162.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/167162.shtml
英文地址,請注明出處:http://en.pswp.cn/news/167162.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

樹莓派上使用Nginx通過內網穿透實現無公網IP訪問內網本地站點

前言 安裝 Nginx&#xff08;發音為“engine-x”&#xff09;可以將您的樹莓派變成一個強大的 Web 服務器&#xff0c;可以用于托管網站或 Web 應用程序。相比其他 Web 服務器&#xff0c;Nginx 的內存占用率非常低&#xff0c;可以在樹莓派等資源受限的設備上運行。同時結合c…

XTU OJ 1146 矩陣乘法學習筆記

原題 題目描述 給你兩個矩陣A(n*k),B(k*m),請求A*B。 輸入 第一行是一個整數K&#xff0c;表示樣例的個數。 每個樣例包含兩個矩陣A和B。 每個矩陣的第一行是兩個整數n,m,(1≤n,m≤10)表示矩陣的行和列 以后的n行&#xff0c;每行m個整數&#xff0c;每個整數的絕對值不超過…

如何讓大模型更好地完成知識圖譜推理?

? 論文標題&#xff1a; Making Large Language Models Perform Better in Knowledge Graph Completion 論文鏈接&#xff1a; https://arxiv.org/abs/2310.06671 代碼鏈接&#xff1a;GitHub - zjukg/KoPA: [Paper][Preprint 2023] Making Large Language Models Perform Be…

node-red - 節點實戰總結1

node-red - 節點實戰總結1 二、功能2.1 循環(for\while) 三、網絡四、序列五、解析六、存儲七、協議7.1 modbus協議7.2 opcua 八、formats8.1 時間格式化與時區轉換 二、功能 2.1 循環(for\while) 安裝節點node-red-contrib-loop-processing,該節點支持三種方式的循環&#xf…

【SpringBoot】 This application has no explicit mapping for 解決方法

This application has no explicit mapping for 解決方法 This application has no explicit mapping for 解決方法一、背景二、原因三、解決方案方式一&#xff1a;方式二&#xff1a; 四、解決 This application has no explicit mapping for 解決方法 一、背景 在SpringBo…

奧特曼不是第一次被開除!離職YC系“被創始人要求離開”

明敏 西風 發自 凹非寺 量子位 | 公眾號 QbitAI 鈕祜祿奧特曼&#xff0c;竟然不是第一次被“掃地出門”&#xff1f;&#xff1f;&#xff01; 沒想到&#xff0c;OpenAI鬧劇剛稍微消停了一點&#xff0c;“前傳”馬上來了。 《華盛頓郵報》從知情人士處獲悉&#xff0c;奧…

java編程:使用遞歸 循環和位運算實現將10進制轉為2進制

1 遞歸 /*** 遞歸&#xff1a;十進制轉二進制* param decimal 待轉換的十進制數* param binary 轉換后的二進制數*/public static void decimalToBinaryByRecursion(int decimal,StringBuilder binary){if(decimal < 0){return;}decimalToBinaryByRecursion(decimal/2,bina…

3D卷積的理解

卷積核不僅需要在高寬這兩個維度上進行滑動&#xff0c;還需要在時間維度上進行滑動

前端設計問題:iframe

居中問題&#xff1a; 嘗試了一般的居中方法&#xff0c;無效果 display: flex;justify-content: center;align-items: center;放到導航欄下面不居中 放到頁面底部還是不居中 Code <iframe id"demo_sanshui" src"demo_sanshui.html" width"120%…

【現場問題】現場的zk出現問題,ES也出現問題,一個一個排查出來,服務器重啟了

湖南現場 zk出現問題ES出現問題了罪魁禍首&#xff0c;服務器重啟 zk出現問題 發現集群出現問題了&#xff0c;想都沒想就直接去重啟了&#xff0c;把三臺服務器都重啟 ES出現問題了 三臺機器只有一臺機器是有ES的&#xff0c;但是看了配置文件&#xff0c;發現這個ES是三臺…

centos7 openssh版本升級至 openssh-9.5p1

公司項目驗收提示ssh版本存在漏洞&#xff0c;要求升級至最高版本&#xff0c;這里做個記錄 默認版本 [rootlocalhost ~]# sshd -v unknown option -- v OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017yum groupinstall -y "Development Tools" yum install -y …

[HCIE] IPSec-VPN (IKE自動模式)

概念&#xff1a; IKE&#xff1a;因特網密鑰交換 實驗目標&#xff1a;pc1與pc2互通 步驟1&#xff1a;R1與R3配置默認路由 R1&#xff1a; ip route-static 0.0.0.0 0.0.0.0 12.1.1.2 R2&#xff1a; ip route-static 0.0.0.0 0.0.0.0 23.1.1.2 步驟2&#xff1a;配ACL…

Postgresql在ubuntu 22.04上遭遇OOM的處理方法

OOM機制就是kill那些占用內存多且優先級低的進程以此來保證操作系統內核的正常運轉&#xff0c;一旦我們關閉OOM可能會導致操作系統內核奔潰。 https://manpages.ubuntu.com/manpages/jammy/en/man1/choom.1.html Linux kernel uses the badness heuristic to select which pro…

工業級5G路由器:穩定性更高,網絡速度更快!

隨著5G技術的發展&#xff0c;5G路由器也越來越受到人們的關注。特別是工業級5G路由器&#xff0c;它的應用范圍更廣&#xff0c;穩定性更高&#xff0c;網絡速度更快&#xff0c;已成為許多企業和工業領域的必備選擇。 一、工業級5G路由器的特點 工業級5G路由器具有很多獨特的…

利用MATLAB進行矩陣運算

一、畫出y1/(x3)的函數曲線&#xff0c;x∈[0, 200]。 程序&#xff1a; x0:0.01:200; y(3x).^(-1); plot(x,y) 結果&#xff1a; 二、生成一個信號&#xff1a;xsin(2*pi*t)cos(4*pi*t) 程序&#xff1a; syms t; xsin(2*pi*t).*cos(4*pi*t); fplot(x,[0 pi]); 結果&…

MyBatis - 自定義 ResultMap 返回數據與數據庫查詢不一致

問題描述 最近遇到一個查詢列表問題&#xff0c;發現實際數據庫查詢 4 條數據&#xff0c;但是經過程序就是 3 條&#xff0c;一開始懷疑是不是自己程序做了去重啥的&#xff0c;一頓排查猛如虎發現并沒有&#xff0c;后來看了下 resultMap 的機制的坑~ <resultMap id"…

electerm 跨平臺的終端 /ssh/sftp 客戶端

文章目錄 electerm功能特性主題配色 electerm 每個程序員基本都離開SSH鏈接工具,目前市場上好用的基本都是收費的 給大家推薦一款國人開發的開源鏈接工具https://github.com/electerm/electerm 到目前為止star已經9.5K了,非常受歡迎 功能特性 支持ssh,telnet,serialport,本地和…

使用AndResGuard報錯:copy res file not in resources.arsc file:Ezi.xml

Android使用AndResGuard進行資源混淆&#xff0c;壓縮。 源碼地址&#xff1a;GitHub - shwenzhang/AndResGuard: proguard resource for Android by wechat team 集成完成后編譯過程中出現如下錯誤&#xff1a; 14:57:05 copy res file not in resources.arsc file:IUk.xml…

CMake API使用指南

文章目錄 CMake 的基本語法和用法1. CMakeLists.txt 基本結構2. 變量和宏3. 條件語句4. 循環語句5. 定義和使用函數6. 導入庫和鏈接庫7. 設置編譯器選項 message1. 顯示普通消息&#xff1a;2. 顯示帶模式的消息&#xff1a;3. 顯示變量值&#xff1a;4. 顯示多行消息&#xff…

ptpd2提示failed to join the multicast group (strerror: No buffer space available)

下載交叉編譯ptpd-ptpd-2.3.1源碼&#xff0c;在IMX6板子上面運行ptpd2提示錯誤如下&#xff1a; rootimx6qsabresd_genvict:~# ./ptpd2 -C -m -i eth0 2023-11-24 14:30:21.484399 ptpd2[6512].startup (info) (___) Configuration OK 2023-11-24 14:30:21.487152 ptpd2…