Spring AOP 學習筆記 之 Advice詳解

學習材料:https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/advice.html

1. 什么是 Advice(通知)

定義:Advice 是 AOP 的核心概念之一,表示在特定的連接點(Join Point)上執行的代碼邏輯。
作用:通過 Advice,可以在方法調用前后、異常拋出時等位置插入自定義邏輯。

2. Advice 的類型

Spring AOP 提供了以下幾種類型的 Advice:

2.1 @Before Advice

@Before 注解用于聲明前置通知(Before Advice),即在目標方法執行之前執行自定義邏輯。
使用場景:適用于需要在方法執行前進行某些操作的場景,例如日志記錄、權限檢查等。
參數:@Before 注解可以接受一個切入點表達式,用于指定哪些方法執行前需要應用該Advice。
方法簽名:@Before 方法可以有參數,但這些參數必須是Spring AOP支持的參數類型,例如 JoinPoint、ProceedingJoinPoint(僅用于 @Around)、JoinPoint.StaticPart 等。

Pointcut表達式包含在注解里的liline版示例:

@Aspect
public class BeforeExample {@Before("execution(* com.xyz.dao.*.*(..))")public void doAccessCheck() {// ...}
}

?

Before注解里包含的是Pointcut的signature,真正的Pointcut表達式在@Pointcut注解里定義。

@Aspect
public class BeforeExample {@Before("com.xyz.CommonPointcuts.dataAccessOperation()")public void doAccessCheck() {// ...}
}@Aspect
public CommonPointcuts {@Pointcut("execute * com.xyz.dao.*.*(..)")public void dataAccessOperation() {}}

?

2.2 @AfterReturing, @AfterThrowing, @After Advice

在Spring AOP中,@AfterReturning、@AfterThrowing 和 @After 是三種不同的通知(Advice)類型,用于在連接點(Join Point)的不同階段執行自定義邏輯。以下是它們的詳細說明:


@AfterReturning
作用:在目標方法成功執行并返回結果后執行。
使用場景:適用于需要在方法成功執行后進行日志記錄、資源清理等操作的場景。
參數:可以接收返回值作為參數,通過returning屬性指定參數名。

    @Pointcut("execution(* org.derek.ctroller.*.*(..))")public void log() {}@AfterReturning(pointcut = "log()", returning = "result")public void afterReturning(Object result) {log.info("LogAspect afterReturning ..., result: {}", result);}

@AfterThrowing
作用:在目標方法拋出異常后執行。
使用場景:適用于需要在方法拋出異常時進行異常處理、日志記錄等操作的場景。
參數:可以接收異常對象作為參數,通過throwing屬性指定參數名。

    @Pointcut("execution(* org.derek.ctroller.*.*(..))")public void log() {}@AfterThrowing(pointcut = "log()", throwing = "exception")public void afterThrowing(Exception exception) {log.info("LogAspect afterThrowing ...", exception);}

@After
作用:無論目標方法是否成功執行,都會在方法執行后執行。
使用場景:適用于需要在方法執行后進行資源清理、日志記錄等操作的場景,不關心方法的執行結果。
參數:不接收任何特定參數。

@Slf4j
@Aspect
@Component
public class LogAspect {@Pointcut("execution(* org.derek.ctroller.*.*(..))")public void log() {}@After("log()")public void after() {log.info("LogAspect after ...");}
}

?

2.3 @Around Advice

Around Advice 是一種特殊的Advice,它會在匹配的方法執行前后運行。它有機會在方法執行前后執行自定義邏輯,并且可以決定方法是否執行、何時執行以及如何執行。
使用場景:Around Advice通常用于需要在方法執行前后共享狀態的場景,例如啟動和停止計時器。
最佳實踐:總是使用滿足需求的最弱形式的Advice。如果Before Advice已經足夠滿足需求,則不要使用Around Advice。


返回類型:Around Advice方法的返回類型應為Object。
參數:方法的第一個參數必須是ProceedingJoinPoint類型。
執行方法:在Around Advice方法體內,必須調用proceed()方法來執行目標方法。調用proceed()方法時,如果不帶參數,會將調用者原始的參數傳遞給目標方法。

@Slf4j
@Aspect
@Component
public class LogAspect {@Pointcut("execution(* org.derek.ctroller.*.*(..))")public void log() {}@Around("log()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("LogAspect around start ...");long start = System.currentMillis();Object result = joinPoint.proceed();log.info("LogAspect around end ...");long end = System.currentMillis();System.out.println("cost time: " + (end-start) + "ms");return result;}
}

3 Advice的執行順序

3.0 測試環境

使用的springboot測試依賴如下:

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>3.3.6</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.3.6</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>3.3.6</version></dependency>

下面測試的Aspect準備攔截Controller的方法,Controller代碼如下:?

@Slf4j
@RestController
public class HelloCtroller {@GetMapping("/hello")public String hello() {log.info("execute method: hello()");return "hello";}@GetMapping("/divide")public Integer divide(@RequestParam("a") Integer a, @RequestParam("b") Integer b) {log.info("execute method: divide(), a: {}, b: {}", a, b);return a/b;}
}

?

3.1 單個Aspect的各Advice執行順序

可以看到我們LogAspect的整體代碼如下:

@Slf4j
@Aspect
@Component
public class LogAspect {@Pointcut("execution(* org.derek.ctroller.*.*(..))")public void log() {}@Around("log()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("LogAspect around start ...");Object result = joinPoint.proceed();log.info("LogAspect around end ...");return result;}@Before("log()")public void before() {log.info("LogAspect before ...");}@AfterReturning(pointcut = "log()", returning = "result")public void afterReturning(Object result) {log.info("LogAspect afterReturning ..., result: {}", result);}@AfterThrowing(pointcut = "log()", throwing = "exception")public void afterThrowing(Exception exception) {log.info("LogAspect afterThrowing ...", exception);}@After("log()")public void after() {log.info("LogAspect after ...");}
}

?

3.1.1 方法正常返回的執行順序

HelloController.hello() 方法會正常執行,并返回String結果。

我們執行 /hello地址的請求:

http://localhost:8080/hello

后臺打印的切面日志如下:

2025-04-15T13:58:26.858+08:00 ?INFO 25536 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet ? ? ? ?: Completed initialization in 1 ms
2025-04-15T13:58:26.888+08:00 ?INFO 25536 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect around start ...
2025-04-15T13:58:26.889+08:00 ?INFO 25536 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect before ...
2025-04-15T13:58:26.889+08:00 ?INFO 25536 --- [nio-8080-exec-1] org.derek.ctroller.HelloCtroller ? ? ? ? : execute method: hello()
2025-04-15T13:58:26.889+08:00 ?INFO 25536 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect afterReturning ..., result: hello
2025-04-15T13:58:26.889+08:00 ?INFO 25536 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect after ...
2025-04-15T13:58:26.889+08:00 ?INFO 25536 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect around end ...

調用方法的正常返回順序如下:

@Around start --> @Before advice --> orginal method??--> @AfterReturing --> @After --> @Around end.

3.1.2 方法碰到異常的執行順序

HelloController.divide(Integer a, Integer b)方法, 當b=0的時候,就會拋出除零異常。

http://localhost:8080/divide?a=2&b=0

執行的Aspect攔截日志如下:

2025-04-15T14:05:03.612+08:00 ?INFO 25536 --- [nio-8080-exec-5] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect around start ...
2025-04-15T14:05:03.613+08:00 ?INFO 25536 --- [nio-8080-exec-5] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect before ...
2025-04-15T14:05:03.613+08:00 ?INFO 25536 --- [nio-8080-exec-5] org.derek.ctroller.HelloCtroller ? ? ? ? : execute method: divide(), a: 2, b: 0
2025-04-15T14:05:03.613+08:00 ?INFO 25536 --- [nio-8080-exec-5] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect afterThrowing ...

java.lang.ArithmeticException: / by zero
?? ?at org.derek.ctroller.HelloCtroller.divide(HelloCtroller.java:25) ~[classes/:na]
?? ?at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
?? ?at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
?? ?at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
?? ?at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
?? ?at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:355) ~[spring-aop-6.1.15.jar:6.1.15]

2025-04-15T14:05:03.620+08:00 ?INFO 25536 --- [nio-8080-exec-5] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect after ...
2025-04-15T14:05:03.622+08:00 ERROR 25536 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] ? ?: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
?

可以看到異常情況下,攔截執行順序如下:

@Around start --> @Before Advice --> orginal method --> @AfterThrowing --> @After (--> @Around end? 因為拋出異常這里不再執行)

3.2 多個Aspect的各Advice的執行順序

1. 默認執行順序
在 Spring AOP 中,默認情況下,Aspect 的執行順序是未定義的。如果多個 Aspect 匹配同一個連接點(Join Point),它們的執行順序可能會根據依賴注入的順序、類加載順序或其他因素動態決定。

2. 通過 @Order 注解控制順序
可以使用 @Order 注解來顯式指定 Aspect 的優先級。
數字越小,優先級越高,越先執行。
示例代碼:

import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;@Aspect
@Order(1) // 優先級最高
public class FirstAspect {// 定義切入點和通知邏輯
}@Aspect
@Order(2) // 次優先級
public class SecondAspect {// 定義切入點和通知邏輯
}

3. 通過實現 Ordered 接口
如果不想使用 @Order 注解,可以實現 org.springframework.core.Ordered 接口,并重寫 getOrder() 方法。
示例代碼:

import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;@Aspect
public class FirstAspect implements Ordered {@Overridepublic int getOrder() {return 1; // 優先級最高}
}@Aspect
public class SecondAspect implements Ordered {@Overridepublic int getOrder() {return 2; // 次優先級}
}

這里為了測試實際的Aspect執行順序,我們使用注解@Order的方式定義了兩個切面:

?ControllerAspect.java, 順序為1.

@Slf4j
@Aspect
@Component
@Order(1) // 優先級,越小越先執行
public class CtrollerAspect {@Pointcut("execution(* org.derek.ctroller.*.*(..))")public void controller() {System.out.println("log");}@Around("controller()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("around advice start ...");Object proceed = joinPoint.proceed();log.info("around advice end ...");return proceed;}@Before("controller()")public void before() {log.info("before advice ...");}@AfterReturning(pointcut = "controller()", returning = "result")public void afterReturning(Object result) {log.info("afterReturning advice ..., result: {}", result);}@AfterThrowing(pointcut = "controller()", throwing = "exception")public void afterThrowing(Exception exception) {log.info("afterThrowing advice ...", exception);}@After("controller()")public void after() {log.info("after advice ...");}
}

LogAspect.java, 順序為2.

@Slf4j
@Aspect
@Component
@Order(2)
public class LogAspect {@Pointcut("execution(* org.derek.ctroller.*.*(..))")public void log() {}@Around("log()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("LogAspect around start ...");Object result = joinPoint.proceed();log.info("LogAspect around end ...");return result;}@Before("log()")public void before() {log.info("LogAspect before ...");}@AfterReturning(pointcut = "log()", returning = "result")public void afterReturning(Object result) {log.info("LogAspect afterReturning ..., result: {}", result);}@AfterThrowing(pointcut = "log()", throwing = "exception")public void afterThrowing(Exception exception) {log.info("LogAspect afterThrowing ...", exception);}@After("log()")public void after() {log.info("LogAspect after ...");}
}

執行相同的hello方法,調用日志如下:

2025-04-15T13:52:45.672+08:00 ?INFO 32288 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet ? ? ? ?: Completed initialization in 0 ms
2025-04-15T13:52:45.705+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.CtrollerAspect ? ? ? ? ?: around advice start ...
2025-04-15T13:52:45.705+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.CtrollerAspect ? ? ? ? ?: before advice ...
2025-04-15T13:52:45.705+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect around start ...
2025-04-15T13:52:45.705+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect before ...
2025-04-15T13:52:45.705+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.ctroller.HelloCtroller ? ? ? ? : execute method: hello()
2025-04-15T13:52:45.705+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect afterReturning ..., result: hello
2025-04-15T13:52:45.707+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect after ...
2025-04-15T13:52:45.707+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.LogAspect ? ? ? ? ? ? ? : LogAspect around end ...
2025-04-15T13:52:45.707+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.CtrollerAspect ? ? ? ? ?: afterReturning advice ..., result: hello
2025-04-15T13:52:45.707+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.CtrollerAspect ? ? ? ? ?: after advice ...
2025-04-15T13:52:45.707+08:00 ?INFO 32288 --- [nio-8080-exec-1] org.derek.aspect.CtrollerAspect ? ? ? ? ?: around advice end ...

可以看到,多個切面執行順序如下:

1) @Order(1)的切面方法??@Around start --> @Before Advice

2) @Order(2)的切面方法 @Around start --> @Before Advice?

3) 執行原始的方法 original method

4) @Order(2)的切面方法 @AfterReturning/@AfterThrowing --> @After --> @Adround end

5)@Order(1)的切面方法 @AfterReturning/@AfterThrowing --> @After --> @Adround end

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

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

相關文章

數智讀書筆記系列029 《代數大腦:揭秘智能背后的邏輯》

《代數大腦:揭秘智能背后的邏輯》書籍簡介 作者簡介 加里F. 馬庫斯(Gary F. Marcus)是紐約大學心理學榮休教授、人工智能企業家,曾創立Geometric Intelligence(后被Uber收購)和Robust.AI公司。他在神經科學、語言學和人工智能領域發表了大量論文,并著有《重啟AI》等多部…

如何看電腦的具體配置?

李升偉 整理 要查看電腦的具體配置&#xff0c;可以通過系統工具、命令行工具或第三方軟件實現&#xff0c;以下是具體方法&#xff1a; 一、系統自帶工具查看&#xff08;無需安裝軟件&#xff09; Windows系統&#xff1a; 系統設置&#xff1a; 右鍵點擊桌面“此電腦”…

開源TTS項目GPT-SoVITS,支持跨語言合成、支持多語言~

簡介 GPT-SoVITS 是一個開源的文本轉語音&#xff08;TTS&#xff09;項目&#xff0c;旨在通過少量語音數據實現高質量的語音合成。其核心理念是將基于變換器的模型&#xff08;如 GPT&#xff09;與語音合成技術&#xff08;如 SoVITS&#xff0c;可能指“唱歌語音合成”&am…

D1084低功耗LDO穩壓器:技術解析與應用設計

引言 在現代電子設計中&#xff0c;低功耗和高效率是至關重要的。D1084是一款5A低功耗低壓差線性穩壓器&#xff08;LDO&#xff09;&#xff0c;以其出色的負載調節能力和快速瞬態響應&#xff0c;成為低電壓微處理器應用的理想選擇。本文將深入解析D1084的技術特性和應用設計…

Log4j詳解:Java日志系統全指南

文章目錄 1. 日志系統簡介1.1 什么是日志1.2 為什么使用日志框架1.3 Java中的常見日志框架 2. Log4j概述2.1 Log4j簡介2.2 Log4j的版本歷史2.3 Log4j與Log4j 2的主要區別 3. Log4j架構與核心組件3.1 Logger&#xff08;日志記錄器&#xff09;3.2 日志級別&#xff08;Level&am…

【信息系統項目管理師】高分論文:論信息系統項目的整合管理(銀行數據倉庫項目)

更多內容請見: 備考信息系統項目管理師-專欄介紹和目錄 文章目錄 正文一、制定項目章程二、制定項目管理計劃三、指導和管理項目的實施四、管理項目知識五、監控項目工作六、實施整體變更控制七、結束項目或階段正文 2023年6月,我以項目經理的身份,參加了 xx銀行xx省分行數…

sql server 預估索引大小

使用deepseek工具預估如下&#xff1a; 問題&#xff1a; 如果建立一個數據類型是datetime的索引&#xff0c;需要多大的空間&#xff1f; 回答&#xff1a; 如果建立一個數據類型是 datetime 的索引&#xff0c;索引的大小取決于以下因素&#xff1a; 索引鍵的大小&#…

干貨 | 高性能 Nginx 優化配置總結

文章目錄 一、前言二、配置優化2.1 并發處理架構優化2.1.1 工作進程配置2.1.2 事件驅動模型 2.2 傳輸效率優化2.2.1 零拷貝技術2.2.2 長連接復用 2.3 緩存體系構建2.3.1 文件描述符緩存2.3.2 代理緩存2.3.3 靜態資源緩存 2.4 協議層深度優化2.4.1 HTTP/2 支持2.4.2 TLS優化 2.5…

ES DSL 常用修改語句

字段值替換修改 修改sql update zyzkwjj set dhreplace(dh,"WS","WSS") where dh like %WS% update zyzkwjj set dh replace(dh, WS, DZ),ztm replace(ztm, WS, DZ),zrz replace(zrz, WS, DZ) where dh like %WS% or ztm like %WS% or zrz like %WS%…

Vue 3 的組合式 API-hooks

Vue 3 的組合式 API 組合式 API 是 Vue 3 的核心特性之一&#xff0c;它允許開發者將組件的邏輯拆分為可復用的函數。組合式 API 的主要特點是 邏輯復用&#xff1a;將邏輯提取到獨立的函數中&#xff0c;方便在多個組件中復用。組織清晰&#xff1a;將相關的邏輯分組&#x…

Web滲透之XSS注入

XSS的類型 1、反射型XSS 我們構建好一個urlXSS的payload&#xff0c;發送給受害者&#xff0c;受害者點擊惡意鏈接后會在受害者的瀏覽器上執行惡意代碼。反射型XSS是一次性的&#xff0c;而且比較容易被發現。通常惡意鏈接會被修改成短鏈接&#xff0c;或釣魚圖片的形式。 2…

【Nginx】Nginx代理Tomcat配置及404問題解決

當Tomcat返回HTTP 404未找到錯誤時&#xff0c;可以通過以下兩種方式設置跳轉到指定地址&#xff1a; ① 在Tomcat應用內部配置錯誤頁面跳轉&#xff08;直接修改Tomcat的Web應用配置&#xff09; ② 在Nginx反向代理層攔截404錯誤并跳轉&#xff08;無需修改Tomcat&#xff0c…

某公司網絡OSPF單區域配置

1.配置背景&#xff1a; xx公司網絡由三臺路由器和一臺交換機組成&#xff0c;現在想要三臺路由器之間通過OSPF實現互連互通。 2.網絡結構如下&#xff1a; 3.具體配置&#xff1a; 3.1路由器 RA 配置&#xff1a; 1.更改主機名稱&#xff1a; Router>en Router#conf t…

電腦知識 | TCP通俗易懂詳解 <一>

目錄 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f68d;什么是TCP/TCP協議 三、&#x1f9cd;?♂為什么TCP可靠 1.&#x1f970;關于可靠 2.&#x1f920;哪里可靠 3.&#x1f393;?圖片的三次握手&#xff0c;四次揮手 4.&#x1f4da;?知識點總結 四、&…

MyBatis 中 Mapper 傳遞參數的多種方法

# MyBatis Mapper 傳遞參數的多種方法及其優勢 在使用 MyBatis 進行數據庫操作時&#xff0c;Mapper 接口的參數傳遞是一個非常基礎但又十分重要的部分。不同的參數傳遞方式適用于不同的場景&#xff0c;合理選擇可以大大提高代碼的可讀性和維護性。本文將詳細介紹幾種常見的 …

Dify 插件開發筆記

Dify 插件開發 開發流程 #mermaid-svg-U9rSMmcbWvcGcFMu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-U9rSMmcbWvcGcFMu .error-icon{fill:#552222;}#mermaid-svg-U9rSMmcbWvcGcFMu .error-text{fill:#552222;st…

行星際激波在日球層中的傳播:Propagation of Interplanetary Shocks in the Heliosphere (第二部分)

行星際激波在日球層中的傳播&#xff1a;Propagation of Interplanetary Shocks in the Heliosphere &#xff08;第一部分&#xff09;- Chapter 1: Introduction & Chapter 2: Basics of Magnetohydrodynamics 行星際激波在日球層中的傳播&#xff1a;Propagation of In…

巴法云平臺-TCP設備云-微信小程序實時接收顯示數據-原理

微信小程序通過WebSocket或HTTP長輪詢連接平臺&#xff08;而非直接使用TCP&#xff09;&#xff01;&#xff01;&#xff01; 物聯網平臺對協議層的一種封裝設計——將底層通信協議&#xff08;如TCP&#xff09;與應用層業務邏輯&#xff08;如主題路由&#xff09;解耦&am…

QT Sqlite數據庫-教程002 查詢數據-上

【1】DQL語句&#xff1a; DQL語句&#xff08;數據查詢語言&#xff09;&#xff0c;用來查詢數據記錄。DQL 基本結構由 SELECT FROM、WHERE、JOIN 等子句構成。DQL 語句并不會改變數據庫&#xff0c;而是讓數據庫將查詢結果發送結果集給客戶端&#xff0c;返回的結果是一張虛…

基礎數學:線性代數與優化理論

本篇文章簡單帶您復習線性代數與優化理論&#xff08;主要是我發表的文章中涉及過的或相關聯的&#xff09; 微積分和概率與統計由此進&#xff1a;基礎數學&#xff1a;微積分和概率與統計-CSDN博客 圖論與信息論由此進&#xff1a;基礎數學&#xff1a;圖論與信息論-CSDN博…