Spring AOP概念及其實現

一、什么是AOP

全稱Aspect Oriented Programming,即面向切面編程,AOP是Spring框架的第二大核心,第一大為IOC。什么是面向切面編程?切面就是指某一類特定的問題,所以AOP也可以稱為面向特定方法編程。例如對異常的統一處理,簡單來說,AOP是一種思想,是對某一類問題的集中處理。AOP的優勢在于程序運行期間在不修改源碼的基礎上對已有的方法進行增強(無侵入性)

二、什么是Spring AOP

AOP是一種思想,它的實現方法有很多,有Spring AOP,也有AspectJ,CGLIB等,Spring AOP只是其中的一種實現方式。

三、Spring AOP入門

首先在pom文件中引入依賴:

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

編寫AOP程序

@Slf4j
@Aspect
@Component
public class TimeAspect {@Around("execution(* com.example.bookManagement.Controller.*.*(..))")public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {//記錄開始時間long startTime = System.currentTimeMillis();//執行原始方法Object result = joinPoint.proceed();//記錄方法執行時間long endTime = System.currentTimeMillis();long elapsedTime = endTime - startTime;log.info(joinPoint.getSignature()+"耗時: {} ms", elapsedTime);return result;}
}

運行程序,觀察Controller方法運行時間:

對程序進行簡單的講解:

@Aspect:標識這是一個切面類。

@Around:環繞通知,在目標方法的前后都會被執行,后面的表達式表示對哪些方法進行增強。

ProceedingJoinPoint.proceed()讓原方法執行。

四、Spring AOP詳解

1.Spring AOP核心

? ? ? ? 1>切點(Pointcut)

? ? ? ? 切點的作用是提供一組規則,告訴程序對哪些方法進行功能增強

????????

@Around("execution(* com.example.bookManagement.Controller.*.*(..))")

什么表達式中的"execution(* com.example.bookManagement.Controller.*.*(..))"就是切點表達式

? ? ? ? 2>.連接點(Join Point)

????????滿足切點表達式規則的方法,就是連接點,也就是可以被AOP控制的方法 。

????????例如:"execution(* com.example.bookManagement.Controller路徑下的方法都是連接點。

? ? ? ? 3>.通知(Advice)

? ? ? ? 通知就是具體要做的工作,指哪些重復的邏輯,也就是共性功能。

? ? ? ? 比如例子中的記錄業務方法的耗時時間,就是通知。

        long startTime = System.currentTimeMillis();//執行原始方法Object result = joinPoint.proceed();//記錄方法執行時間long endTime = System.currentTimeMillis();long elapsedTime = endTime - startTime;log.info(joinPoint.getSignature()+"耗時: {} ms", elapsedTime);

? ? ? ? 4>.切面(Aspect)

? ? ? ? 切面=切點+通知

? ? ? ? 通過切面能夠描述當前AOP程序需要針對哪些方法,在什么時候執行什么樣的操作,切面即包含了通知邏輯的定義,也包含了連接點的定義。即例子中的如下代碼:

 @Around("execution(* com.example.bookManagement.Controller.*.*(..))")public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {//記錄開始時間long startTime = System.currentTimeMillis();//執行原始方法Object result = joinPoint.proceed();//記錄方法執行時間long endTime = System.currentTimeMillis();long elapsedTime = endTime - startTime;log.info(joinPoint.getSignature()+"耗時: {} ms", elapsedTime);return result;}

? ? ? ? 切面所在的類,我們一般稱為切面類(被@Aspect標注的類)

2.通知類型

上述的@Around就是其中一類的通知類型。Spring中的AOP通知類型有以下幾類:

@Around:環繞通知,此注解標注的通知方法在目標方法前后都被執行

@Before:前置通知,此注解標注的方法在目標方法前被執行。

@After:后置通知,此注解標注的方法在目標方法后被執行,無論是否有異常都會執行。

@AfterReturning:返回后通知,此注解標注的通知方法在目標方法后被執行,有異常不會執行。

@AfterThrowing:異常后通知,此注解的通知方法發生異常后執行。

下面是代碼案例:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class AspectDemo {//前置通知@Before("execution(* com.example.aop.Controller.*.*(..))")public void before() {log.info("執行Before方法");}//后置通知@After("execution(* com.example.aop.Controller.*.*(..))")public void after() {log.info("執行After方法");}//返回后通知@AfterReturning("execution(* com.example.aop.Controller.*.*(..))")public void afterReturning() {log.info("執行AfterReturn 方法");}//拋出異常后通知@AfterThrowing("execution(* com.example.aop.Controller.*.*(..))")public void afterThrowing() {log.info("執行AfterThrowing方法");}//環繞通知@Around("execution(* com.example.aop.Controller.*.*(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around方法開始執行");Object result = joinPoint.proceed();log.info("Around方法執行");return result;}
}

添加以下測試程序:

@RequestMapping("/test")
@RestController
public class TestController {@RequestMapping("/t1")public String test1() {return "test1";}@RequestMapping("/t2")public String test2() {int a = 10/0;return "test2";}
}

運行t1:

可以看出執行順序如下:

運行t2,觀察異常情況:

程序發生異常情況下:

@AfterReturning標識的通知方法不會執行,@AfterThrowing標識的通知方法執行了

@Around第一個方法執行了,第二個沒有執行。

3.@PointCut

什么代碼存在一個問題,就是存在大量的切點表達式execution(* com.example.aop.Controller.*.*(..)),Spring提供了@PointCut注解,把公共的切點表達式提取出來,需要時引入該切點表達式即可。

上述代碼可以改為:

@Slf4j
@Aspect
@Component
public class AspectDemo {@Pointcut("execution(* com.example.aop.Controller.*.*(..))")public void pointcut() {}//前置通知@Before("pointcut()")public void before() {log.info("執行Before方法");}//后置通知@After("pointcut()")public void after() {log.info("執行After方法");}//返回后通知@AfterReturning("pointcut()")public void afterReturning() {log.info("執行AfterReturn 方法");}//拋出異常后通知@AfterThrowing("pointcut()")public void afterThrowing() {log.info("執行AfterThrowing方法");}//環繞通知@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around方法開始執行");Object result = joinPoint.proceed();log.info("Around方法執行");return result;}
}

但是這種方式只適用于當前切面,如果當其他切面要使用時,就需要將private改為public,而且引用方式改為:全限定類名.方法名()

例如:

@Slf4j
@Aspect
@Component
public class AspectDemo2 {@Before("com.example.aop.AspectDemo.AspectDemo.pointcut()")public void before() {log.info("執行before方法:Aspect Demo2");}
}

4.切面優先級@Order

當我們在一個項目中,定義了多個切面類,而且這些切面類的多個切入點都匹配了同一個目標方法,當目標方法運行時,這些切面類中的通知方法都會執行,那么這幾個通知方法的執行順序是什么樣子的呢。

這里直接給出結果:

存在多個切面類時,默認按照切面類的類名字母排序:

@Before:字母排名靠前的先執行。

@After:字母排名靠前的后執行。

這種方式并不方便管理,因為類名往往是不考慮首字母的。

Spring提供了一個全新的注解,來控制這些切面通知的執行順序:@Order

@Order(數字):

@Before:數字小的先執行;

@After:數字大的先執行。

5.切點表達式

切點表達式常見的有兩種表達式

1.execution(......):根據方法的簽名來匹配。

2.@annotation(.....):根據注解匹配。

1.execution表達式

最常見的切點表達式,用來匹配方法,語法為:

execution(<訪問修飾符> <返回類型> <包名.類名.方法(方法參數)> <異常>)

其中訪問修飾符和異常可以省略。

其中:*表示任意一層/一個包/參數,..表示任意多個包或者參數。

2.@annotation注解

相較于execution表達式,@annotation注解適用于多個無規則的方法,比如TestController中的t1和User Controller中的t2.。

實現步驟:

1.編寫自定義注解。

2.使用@annotation表達式來描述切點。

3.在連接點的方法上添加自定義注解。

例子:

先定義Controller:

@RequestMapping("/user")
public class UserController {@RequestMapping("/u1")public String u1() {return "u1";}@RequestMapping("/u2")public String u2() {return "u2";}
}
@RequestMapping("/test")
@RestController
public class TestController {@RequestMapping("/t1")public String test1() {return "test1";}@RequestMapping("/t2")public String test2() {int a = 10/0;return "test2";}
}

創建自定義注解類@MyAspect

package com.example.aop.MyAspect;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)
public @interface MyAspect {
}

解釋:@Target標識了Annotation所修飾的對象范圍,即注解可以用在什么地方

@Retention標明注解的生命周期。

定義切面類

使用@annotation切點表達式定義切點,只對@MyAspect生效

@Slf4j
@Component
@Aspect
public class MyAspectDemo {@Before("@annotation(com.example.aop.MyAspect.MyAspect)")public void before() {log.info("MyAspect->before method");}@After("@annotation(com.example.aop.MyAspect.MyAspect)")public void after() {log.info("MyAspect->after method");}
}

添加自定義注解

在TestController中的t1和UserController中的u1這兩個方法上添加自定義注解@MyAspect,其他的不添加。

@RequestMapping("/user")
public class UserController {@MyAspect@RequestMapping("/u1")public String u1() {return "u1";}@MyAspect@RequestMapping("/t1")public String test1() {return "test1";}

運行程序進行測試:

可以看出切面通知被執行了

運行其他的方法:

可以看出并沒有執行切面通知

經典面試題:

Spring AOP的實現方式:

1.基于注解@Aspect

2.基于自定義注解(參考@annotation部分的內容)

3.基于Spring API(提供xml配置的方式,但是自從SpringBoot廣泛使用后,這種方式幾乎就看不見了)

4.基于代理來實現(更加久遠的方式,寫法繁瑣)

五、Spring AOP原理

Spring AOP是基于動態代理來實現AOP的,首先先了解代理模式:

1.代理模式

也叫委托模式,為其他對象提供一種代理以控制對這個對象的訪問,它的作用就是提供一個代理類,讓我們在調用目標方法的時候,不再是直接對目標方法進行調用,就是提供代理類簡介訪問。

根據代理的創建時期,代理模式可以分為靜態代理動態代理

靜態代理:由程序員創建代理類或特定工具自動生成源代碼再對其進行編譯,在程序運行前代理類的.class文件就已經存在了。

動態代理:在程序運行時,運用反射機制動態創建而成。

靜態代理

靜態代理由于代碼冗余度高,靈活性低,無法在沒有接口的情況下使用,所以使用的頻率較低。

下面是一個簡單的例子:

就用戶注冊的例子:

// 公共接口:用戶服務
public interface UserService {void register(String username, String password); // 注冊方法
}
// 真實目標對象:具體用戶服務實現
public class RealUserService implements UserService {@Overridepublic void register(String username, String password) {System.out.println("真實注冊邏輯:用戶 " + username + " 注冊成功(密碼已加密)");// 這里可以是復雜的數據庫操作、校驗等真實邏輯}
}
/ 靜態代理類:增強用戶服務
public class UserServiceProxy implements UserService {private final UserService realUserService; // 持有真實對象的引用// 構造方法傳入真實對象public UserServiceProxy(UserService realUserService) {this.realUserService = realUserService;}// 重寫接口方法,織入增強邏輯@Overridepublic void register(String username, String password) {// 前置增強:注冊前的日志記錄System.out.println("【代理前置】開始處理注冊請求:用戶 " + username);// 調用真實對象的方法(核心業務)realUserService.register(username, password);// 后置增強:注冊后的日志記錄System.out.println("【代理后置】注冊流程結束,已記錄操作日志");}
}

運行一個例子:

public class Client {public static void main(String[] args) {// 1. 創建真實對象UserService realService = new RealUserService();// 2. 創建代理對象,傳入真實對象UserService proxyService = new UserServiceProxy(realService);// 3. 通過代理對象調用方法(觸發代理邏輯)proxyService.register("張三", "123456");}
}

結果:

【代理前置】開始處理注冊請求:用戶 張三
真實注冊邏輯:用戶 張三 注冊成功(密碼已加密)
【代理后置】注冊流程結束,已記錄操作日志

動態代理

相比于靜態代理,動態代理就更加靈活,我們不需要針對每一個目標對象都單獨創建一個代理

對象,而是把這個創建對象的工作推遲到程序運行時由JVM來實現,也就是說動態代理在程序運行時,根據需要動態創建生成。

常見的實現方式有兩種:

1.JDK動態代理

2.CGLIB動態代理

JDK動態代理

實現步驟:

1.定義一個接口及其實現類

2.自定義InvocationHandler并重寫invoke方法,在invoke方法中調用目標方法并自定義一些處理邏輯。

3.通過Proxy.newProxyInstance(ClassLoder loder,Class<?>[] interfaces, InvaocationHandler h)方法創建代理對象。

定義JDK動態代理類

實現InvocationHandler接口

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class JDKInvocationHandler implements InvocationHandler {//目標對象private Object target;public JDKInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//增強代理內容System.out.println("我是代理者");Object reVal = method.invoke(target, args);//代理增強內容System.out.println("代理結束");return reVal;}
}

創建一個代理對象并使用

import com.example.aop.HouseSubject.HouseSubject;
import com.example.aop.HouseSubject.RealHouseSubject;
import com.example.aop.JDK.JDKInvocationHandler;
import org.springframework.cglib.proxy.Proxy;public class Main {public static void main(String[] args) {HouseSubject target = new RealHouseSubject();//創建一個代理類HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{HouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();}
}

HouseSubject和RealHouseSubject的代碼:

public interface HouseSubject {void rentHouse();void saleHouse();}
public class RealHouseSubject implements HouseSubject {@Overridepublic void rentHouse() {System.out.println("房屋擁有者出租房子");}@Overridepublic void saleHouse() {System.out.println("房屋擁有者出售房子");}
}

運行結果:

代碼講解:

1.InvocationHandler

InvocationHandler接口是java動態代理的關鍵接口之一,它定義了一個單一方法invoke(),用于處理被代理對象的方法調用。

public interface InvocationHandler
extends Callback
{/*** @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}

參數說明:proxy:代理對象;method:代理對象需要實現的方法,即其中需要重寫的方法;

args:mothed所對應方法的參數。

2.Proxy

Proxy類中使用頻率最高的方法是:newProxyInstance(),這個方法主要用來生成一個代理對象。

    public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) {try {Class clazz = getProxyClass(loader, interfaces);return clazz.getConstructor(new Class[]{ InvocationHandler.class }).newInstance(new Object[]{ h });} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);}}

參數含義:

loder:類加載器,用于加載代理對象。

interfaces:被代理類實現的一些接口。

h:實現了InvocationHandler接口的對象。

CGLIB動態代理

JDK有一個很致命的缺點就是只能代理實現了接口的類,在沒有實現接口的情況下,我們就可以使用CGLIB來解決問題。

實現步驟:

1.定義一個被代理類;

2.自定義MethodInterceptor并重寫intercept方法,intercept用于增強目標方法,和JDK中的invoke方法類似。

3.通過Enhancer類中的create()創建代理類。

添加依賴:

CGLIB是一個開源項目,使用的話需要引入依賴

 <dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>

自定義MethodInterceptor(方法攔截器)

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class CGLIBInterceptor implements MethodInterceptor {private Object target;public CGLIBInterceptor(Object target) {this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("代理者開始代理");Object result = methodProxy.invoke(target, objects);System.out.println("代理者結束代理");return result;}
}

注意導入的包為import org.springframework.cglib.proxy.MethodProxy;

創建代理類并使用:

public class Main {public static void main(String[] args) {HouseSubject target = new RealHouseSubject();HouseSubject proxy = (HouseSubject) Enhancer.create(target.getClass(), new CGLIBInterceptor(target));proxy.rentHouse();}
}

運行結果:

代碼講解:

1.MethodInterceptor

和JDK方式中的InvocationHandler類似,它只是定義了一個方式Intercept,用于增強目標方法。

2.Enhancer.create()

用來生成一個代理對象。

面試題:

Spring使用的哪種方式:

默認proxyTargetClass (源碼中的一個重要參數)?false,如果實現了接口,使用JDK;普通類,使用CGLIB

Spring Boot使用哪種方式:

在SpringBoot2.0以后,proxyTargetClass默認為true,默認使用CGLIB。

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

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

相關文章

強化學習_Paper_2017_Curiosity-driven Exploration by Self-supervised Prediction

paper Link: ICM: Curiosity-driven Exploration by Self-supervised Prediction GITHUB Link: 官方: noreward-rl 1- 主要貢獻 對好奇心進行定義與建模 好奇心定義&#xff1a;next state的prediction error作為該state novelty 如果智能體真的“懂”一個state&#xff0c;那…

spring中的@Configuration注解詳解

一、概述與核心作用 Configuration是Spring框架中用于定義配置類的核心注解&#xff0c;旨在替代傳統的XML配置方式&#xff0c;通過Java代碼實現Bean的聲明、依賴管理及環境配置。其核心作用包括&#xff1a; 標識配置類&#xff1a;標記一個類為Spring的配置類&#xff0c;…

7.計算機網絡相關術語

7. 計算機網絡相關術語 ACK (Acknowledgement) 確認 ADSL (Asymmetric Digital Subscriber Line) 非對稱數字用戶線 AP (Access Point) 接入點 AP (Application) 應用程序 API (Application Programming Interface) 應用編程接口 APNIC (Asia Pacific Network Informatio…

Hadoop 集群基礎指令指南

目錄 &#x1f9e9; 一、Hadoop 基礎服務管理指令 ?? 啟動 Hadoop ?? 關閉 Hadoop &#x1f9fe; 查看進程是否正常運行 &#x1f4c1; 二、HDFS 常用文件系統指令 &#x1f6e0;? 三、MapReduce 作業運行指令 &#x1f4cb; 四、集群狀態監控指令 &#x1f4a1; …

【MySQL數據庫】事務

目錄 1&#xff0c;事務的詳細介紹 2&#xff0c;事務的屬性 3&#xff0c;事務常見的操作方式 1&#xff0c;事務的詳細介紹 在MySQL數據庫中&#xff0c;事務是指一組SQL語句作為一個指令去執行相應的操作&#xff0c;這些操作要么全部成功提交&#xff0c;對數據庫產生影…

一、OrcaSlicer源碼編譯

一、下載 1、OrcaSlicer 2.3.0版本的源碼 git clone https://github.com/SoftFever/OrcaSlicer.git -b v2.3.0 二、編譯 1、在OrcaSlicer目錄運行cmd窗口&#xff0c;輸入build_release.bat 2、如果出錯了&#xff0c;可以多運行幾次build_release.bat 3、在OrcaSlicer\b…

港口危貨儲存單位主要安全管理人員考試精選題目

港口危貨儲存單位主要安全管理人員考試精選題目 1、危險貨物儲存場所的電氣設備應符合&#xff08; &#xff09;要求。 A. 防火 B. 防爆 C. 防塵 D. 防潮 答案&#xff1a;B 解析&#xff1a;港口危貨儲存單位存在易燃易爆等危險貨物&#xff0c;電氣設備若不防爆&…

格雷希爾用于工業氣體充裝站的CZ系列氣罐充裝轉換連接器,其日常維護有哪些

格雷希爾氣瓶充裝連接器&#xff0c;長期用于壓縮氣體的快速充裝和壓縮氣瓶的氣密性檢測&#xff0c;需要進行定期的維護&#xff0c;為每一次的充裝提供更好的連接。下列建議的幾點維護準則適用于格雷希爾所有充注接頭&#xff0c;請非專業人士不要隨意拆卸連接器。 格雷希爾氣…

Java 多線程進階:什么是線程安全?

在多線程編程中&#xff0c;“線程安全”是一個非常重要但又常被誤解的概念。尤其對于剛接觸多線程的人來說&#xff0c;不理解線程安全的本質&#xff0c;容易寫出“偶爾出錯”的代碼——這類 bug 往往隱蔽且難以復現。 本文將用盡可能通俗的語言&#xff0c;從三個角度解釋線…

MSO-Player:基于vlc的Unity直播流播放器,支持主流RTSP、RTMP、HTTP等常見格式

MSO-Player 基于libVLC的Unity視頻播放解決方案 支持2D視頻和360度全景視頻播放的Unity插件 &#x1f4d1; 目錄 &#x1f3a5; MSO-Player &#x1f4cb; 功能概述&#x1f680; 快速入門&#x1f4da; 關鍵組件&#x1f4dd; 使用案例&#x1f50c; 依賴項&#x1f4cb; 注意…

navicat中導出數據表結構并在word更改為三線表(適用于navicat導不出doc)

SELECTCOLUMN_NAME 列名,COLUMN_TYPE 數據類型,DATA_TYPE 字段類型,IS_NULLABLE 是否為空,COLUMN_DEFAULT 默認值,COLUMN_COMMENT 備注 FROMINFORMATION_SCHEMA.COLUMNS WHEREtable_schema db_animal&#xff08;數據庫名&#xff09; AND table_name activity&#xff08;…

docker學習筆記6-安裝wordpress

一、創建自定義網絡、查看網絡 docker netword create blog docker network ls 二、 啟動mysql容器 啟動命令&#xff1a; docker run -d -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e MYSQL_DATABASEwordpress \ -v mysql-data:/var/lib/mysql \ -v /app/myconf:/etc…

03_Mybatis-Plus LambadaQueryWrapper 表達式爆空指針異常

&#x1f31f; 03_MyBatis-Plus LambdaQueryWrapper 爆出空指針異常的坑點分析 ? 場景描述 來看一段常見的 MyBatis-Plus 查詢寫法&#xff0c;是否存在問題&#xff1f; Page<VideoInfoVo> videoInfoVosPage videoMapper.selectPage(page, new LambdaQueryWrapper&…

WEB安全--社會工程--SET釣魚網站

1、選擇要釣魚的網站 2、打開kali中的set 3、啟動后依次選擇&#xff1a; 4、輸入釣魚主機的地址&#xff08;kali&#xff09;和要偽裝的網站域名&#xff1a; 5、投放釣魚網頁&#xff08;服務器域名:80&#xff09; 6、獲取賬號密碼

Ethan獨立開發產品日報 | 2025-04-29

1. mrge 代碼審查的光標 mrge 是一個由人工智能驅動的代碼審查平臺&#xff0c;能夠自動審核拉取請求&#xff08;PR&#xff09;&#xff0c;為人工審查員提供超級能力。它是像 cal.com 和 n8n 這樣快速發展的團隊的首選工具。 關鍵詞&#xff1a;mrge, 代碼審查, AI驅動, …

ubuntu22.04 qemu arm64 環境搭建

目錄 創建 安裝 Qemu 啟動 # 進入qemu虛擬機后執行 qemu編譯器安裝 創建 qemu-img create ubuntu22.04_arm64.img 40G 安裝 qemu-system-aarch64 -m 4096 -cpu cortex-a57 -smp 4 -M virt -bios QEMU_EFI.fd -nographic -drive ifnone,fileubuntu-22.04.5-live-server-a…

安全生產知識競賽宣傳口號160句

1. 安全生產是責任&#xff0c;每個人都有責任 2. 安全生產是保障&#xff0c;讓我們遠離危險 3. 安全生產是團結&#xff0c;共同守護每一天 4. 注重安全&#xff0c;守護明天 5. 安全生產無小事&#xff0c;關乎千家萬戶 6. 安全第一&#xff0c;人人有責 7. 安全生產無差別&…

Python 虛擬環境管理:venv 與 conda 的選擇與配置

文章目錄 前言一、虛擬環境的核心價值1.1 依賴沖突的典型場景1.2 隔離機制實現原理 二、venv 與 conda 的架構對比2.1 工具定位差異2.2 性能基準測試&#xff08;以創建環境 安裝 numpy 為例&#xff09; 三、venv 的配置與最佳實踐3.1 基礎工作流3.2 多版本 Python 管理 四、…

【自然語言處理與大模型】如何獲取特定領域的微調數據集?

在特定領域中&#xff0c;數據集通常由提出需求的一方提供。然而&#xff0c;在某些情況下&#xff0c;如果他們未能提供所需的數據&#xff0c;或者你正在獨立開展一個項目&#xff0c;并且需要相應的數據來推進工作&#xff0c;這時你應該怎么辦呢&#xff1f;本文提供一種思…

Map系列之ConcurrentHashMap源碼分析:高并發場景下的性能密碼

引言&#xff1a;當線程安全成為剛需 1.1 并發時代的Map困境 經典案例&#xff1a;電商秒殺系統超賣事故分析&#xff08;附線程堆棧截圖&#xff09;傳統方案缺陷&#xff1a;synchronizedMap的吞吐量陷阱&#xff08;JMH測試數據對比&#xff09;ConcurrentHashMap的定位&a…