SpringBoot JAR 啟動原理

文章目錄

    • 版本
    • 概述
    • JAR 包結構
    • `MANIFEST.MF` 描述文件
    • JarLauncher
      • `Archive` 接口
      • `launch` 方法
      • `Handlers.register()` 方法
      • `getClassPathUrls` 方法
      • `createClassLoader` 方法
    • 時序圖
    • 參考

版本

  • Java 17
  • SpringBoot 3.2.4

概述

JAR 啟動原理可以簡單理解為“java -jar的啟動原理”

SpringBoot 提供了 Maven 插件 spring-boot-maven-plugin,可以將 SpringBoot 項目打包成 JAR 包,這個跟普通 JAR 包有所不同

  • 普通 JAR 包:可以被其他項目引用,解壓后就是包名,包里就是代碼
  • SpringBoot 打包的 JAR 包:只能運行,不能被其他項目依賴,包里 \BOOT-INF\classes 目錄才是代碼

使用 maven package 指令執行打包命令,將項目打包成 JAR 包,根據 pom.xml 文件中的 nameversion 標簽作為 JAR 包名稱,比如項目的
pom.xml 配置文件有 <name>springboot-demo</name><version>0.0.1-SNAPSHOT</version>,執行 maven package 命令之后打包出來之后為 springboot-demo-0.0.1-SNAPSHOT.jarspringboot-demo-0.0.1-SNAPSHOT.jar.original

  • springboot-demo-0.0.1-SNAPSHOT.jar 之類的 JAR 包:spring-boot-maven-plugin 生成的 JAR 包。包含了應用的第三方依賴,SpringBoot 相關的類,存在嵌套的 JAR 包,稱之為 executable jar 或 fat jar。也就是最終可運行的 SpringBoot 的 JAR 包。可以直接執行 java -jar 指令啟動
  • springboot-demo-0.0.1-SNAPSHOT.jar.original 之類的 JAR 包:默認 maven-jar-plugin 生成的 JAR 包,僅包含編譯用的本地文件。也就是打包之前生成的原始 JAR 包,僅包含你項目本身的 class 文件和資源文件,不包含依賴項,也不具備 Spring Boot 的啟動結構。通常由 Spring Boot Maven 插件在打包過程中中間步驟生成,Spring Boot 會在這個基礎上重打包(repackage)為可運行的 JAR 文件。

JAR 包結構

springboot-demo-0.0.1-SNAPSHOT.jar 之類的 JAR 包中通常包括 BOOT-INFMETA-INForg 三個文件夾

  • META-INF:通過 MANIFEST.MF 文件提供 jar 包的元數據,聲明了 JAR 的啟動類
  • org:為 SpringBoot 提供的 spring-boot-loader 項目,它是 java -jar 啟動 Spring Boot 項目的秘密所在
  • BOOT-INF/lib:SpringBoot 項目中引入的依賴的 JAR 包,目的是解決 JAR 包里嵌套 JAR 的情況,如何加載到其中的類
  • BOOT-INF/classes:Java 類所編譯的 .class、配置文件等等

應用程序類應該放在嵌套的BOOT-INF/classes目錄中。依賴項應該放在嵌套的BOOT-INF/lib目錄中。

├── BOOT-INF // 文件目錄存放業務相關的,包括業務開發的類和配置文件,以及依賴的 JAR
│   ├── classes
│   │   ├── application.yaml
│   │   └── com
│   │       └── example
│   │           └── springbootdemo
│   │               ├── OrderProperties.class
│   │               ├── SpringbootDemoApplication.class // 啟動類
│   │               ├── SpringbootDemoApplication$OrderPropertiesCommandLineRunner.class
│   │               ├── SpringbootDemoApplication$ValueCommandLineRunner.class
│   │               ├── SpringMVCConfiguration.class
│   │               └── vo
│   │                   └── UserVO.class
│   ├── classpath.idx
│   ├── layers.idx
│   └── lib
│       ├── spring-aop-6.1.6.jar
│       ├── spring-beans-6.1.6.jar
│       ├── spring-boot-3.2.5.jar
│       ├── spring-boot-autoconfigure-3.2.5.jar
│       ├── spring-boot-jarmode-layertools-3.2.5.jar
├── META-INF // MANIFEST.MF 描述文件和 maven 的構建信息
│   ├── MANIFEST.MF
│   ├── maven
│   │   └── com.example
│   │       └── springboot-demo
│   │           ├── pom.properties // 配置文件
│   │           └── pom.xml
│   ├── services
│   │   └── java.nio.file.spi.FileSystemProvider
│   └── spring-configuration-metadata.json
└── org└── springframework└── boot└── loader // SpringBoot loader 相關類├── jar│   ├── ManifestInfo.class│   ├── MetaInfVersionsInfo.class├── jarmode│   └── JarMode.class├── launch│   ├── Archive.class│   ├── Archive$Entry.class├── log│   ├── DebugLogger.class│   ├── DebugLogger$DisabledDebugLogger.class│   └── DebugLogger$SystemErrDebugLogger.class├── net│   ├── protocol│   │   ├── Handlers.class│   │   ├── jar│   │   │   ├── Canonicalizer.class│   │   │   ├── Handler.class│   │   └── nested│   │       ├── Handler.class│   │       ├── NestedLocation.class│   └── util│       └── UrlDecoder.class├── nio│   └── file│       ├── NestedByteChannel.class│       ├── NestedByteChannel$Resources.class├── ref│   ├── Cleaner.class│   └── DefaultCleaner.class└── zip├── ByteArrayDataBlock.class├── CloseableDataBlock.class

MANIFEST.MF 描述文件

MANIFEST.MF 是 Java JAR(Java Archive)文件中的一個核心元數據文件,用于描述 JAR 包的配置信息和依賴關系。它位于 JAR 文件內部的 META-INF/ 目錄下,是 JVM 啟動可執行 JAR 或加載依賴的關鍵依據。

java -jar 命令引導的具體啟動類必須配置在 MANIFEST.MF 描述文件中的 Main-Class 屬性中,該命令用來引導標準執行的 JAR 文件,讀取的就是 MANIFEST.MF 文件中的 Main-Class 屬性值,Main-Class 屬性就是定義包含了 main 方法的類代表了應用程序執行入口類

Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.3.0
Build-Jdk-Spec: 19
Implementation-Title: springboot-demo
Implementation-Version: 0.0.1-SNAPSHOT
Main-Class: org.springframework.boot.loader.launch.JarLauncher
Start-Class: com.example.springbootdemo.SpringbootDemoApplication
Spring-Boot-Version: 3.2.5
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
  • Main-Class:定義程序入口類,格式為完全限定類名(含包名),JVM 通過此屬性找到 public static void main(String[] args) 方法啟動程序。設置為 spring-boot-loader 項目的 JarLauncher 類,進行 SpringBoot 應用的啟動。
  • Start-Class:SpringBoot 規定的主啟動類
  • Class-Path:指定依賴的 JAR 文件或目錄,路徑用空格分隔
    • 路徑相對于 JAR 文件的位置(非當前工作目錄)
    • 依賴需放在 JAR 同級目錄的指定路徑下
  • Manifest-Version:指定清單文件版本
  • Created-By:生成 JAR 的工具信息(如 JDK 版本或構建工具)
  • Implementation-Version:JAR 的版本號(用于版本管理)

雖然 Start-Class 已經指向了主啟動類路徑,但是不能直接啟動

  • 原因一:因為在 JAR 包中,主啟動類并不在這個路徑上,而是在在 BOOT-INF/classes 目錄下,不符合 Java 默認的 JAR 包的加載規則。因此,需要通過 JarLauncher 啟動加載。
  • 原因二:Java 規定可執行器的 JAR 包禁止嵌套其它 JAR 包。但是可以看到 BOOT-INF/lib 目錄下,實際有 SpringBoot 應用依賴的所有 JAR 包。因此,spring-boot-loader 項目自定義實現了 ClassLoader 實現類 LaunchedURLClassLoader,支持加載 BOOT-INF/classes 目錄下的 .class 文件,以及 BOOT-INF/lib 目錄下的 jar 包。

JarLauncher

JarLauncherSpring Boot 框架中用于啟動可執行 JAR 文件的核心類,屬于 org.springframework.boot.loader 包。它的核心作用是為 Spring Boot 的“胖 JAR”(Fat JAR)提供自定義的啟動機制,解決傳統 JAR 無法直接加載嵌套依賴的問題。

位于 JAR 包中的 org.springframework.boot.loader.launch.JarLauncher

繼承類:Launcher -> ExecutableArchiveLauncher -> JarLauncher

public class JarLauncher extends ExecutableArchiveLauncher {public JarLauncher() throws Exception {}protected JarLauncher(Archive archive) throws Exception {super(archive);}protected boolean isIncludedOnClassPath(Archive.Entry entry) {return isLibraryFileOrClassesDirectory(entry);}protected String getEntryPathPrefix() {return "BOOT-INF/";}static boolean isLibraryFileOrClassesDirectory(Archive.Entry entry) {String name = entry.name();return entry.isDirectory() ? name.equals("BOOT-INF/classes/") : name.startsWith("BOOT-INF/lib/");}public static void main(String[] args) throws Exception {(new JarLauncher()).launch(args);}
}

通過 (new JarLauncher()).launch(args) 創建 JarLauncher 對象,調用 launch 方法進行啟動,整體邏輯還是通過父類的父類 Laucher 所提供。

Archive 接口

根據類圖可知,JarLauncher 繼承于 ExecutableArchiveLauncher 類,在 ExecutableArchiveLauncher 類源碼中有對 Archive 對象的構造

public abstract class ExecutableArchiveLauncher extends Launcher {public ExecutableArchiveLauncher() throws Exception {this(Archive.create(Launcher.class));}
}

Archive 接口,是 spring-boot-loader 項目抽象出來的用來統一訪問資源的接口,ExplodedArchive 是針對目錄的 Archive 實現類,JarFileArchive 是針對 JAR 的 Archive 實現類,所以根據 isDirectory 方法進行判斷。

  • Archive 概念即歸檔文檔概念,在 Linux 下比較常見
  • 通常就是一個 tar/zip 格式的壓縮包
  • JAR 是 zip 格式
public interface Archive extends AutoCloseable {static Archive create(Class<?> target) throws Exception {return create(target.getProtectionDomain());}static Archive create(ProtectionDomain protectionDomain) throws Exception {CodeSource codeSource = protectionDomain.getCodeSource();URI location = codeSource != null ? codeSource.getLocation().toURI() : null;// 拿到當前 classpath 的絕對路徑String path = location != null ? location.getSchemeSpecificPart() : null;if (path == null) {throw new IllegalStateException("Unable to determine code source archive");} else {return create(new File(path));}}static Archive create(File target) throws Exception {if (!target.exists()) {throw new IllegalStateException("Unable to determine code source archive from " + target);} else {return (Archive)(target.isDirectory() ? new ExplodedArchive(target) : new JarFileArchive(target));}}
}

launch 方法

在其父類 Laucher 中可以看出,launcher 方法可以讀取 JAR 包中的類加載器,保證 BOOT-INF/lib 目錄下的類和 BOOT-classes 內嵌的 jar 中的類能夠被正常加載到,之后執行 Spring Boot 應用的啟動。

public abstract class Launcher {protected void launch(String[] args) throws Exception {// 如果當前不是解壓模式(!this.isExploded()),則注冊處理器(Handlers.register())if (!this.isExploded()) {Handlers.register();}try {// 創建類加載器(ClassLoader)用于加載類路徑上的類ClassLoader classLoader = this.createClassLoader((Collection)this.getClassPathUrls());// 根據系統屬性 "jarmode" 判斷是否使用特定的 JAR 模式運行器類名String jarMode = System.getProperty("jarmode");String mainClassName = this.hasLength(jarMode) ? JAR_MODE_RUNNER_CLASS_NAME : this.getMainClass();// 使用創建的類加載器和主類名調用 launch 方法啟動應用this.launch(classLoader, mainClassName, args);} catch (UncheckedIOException var5) {UncheckedIOException ex = var5;throw ex.getCause();}}protected void launch(ClassLoader classLoader, String mainClassName, String[] args) throws Exception {Thread.currentThread().setContextClassLoader(classLoader);Class<?> mainClass = Class.forName(mainClassName, false, classLoader);Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);mainMethod.setAccessible(true);mainMethod.invoke((Object)null, args);}
}

Handlers.register() 方法

逐步分析 launcher 方法,首先方法中調用的 Handlers.register() 方法,用于動態注冊自定義協議處理器包,并確保 URL 流處理器緩存被正確刷新。

public final class Handlers {private static final String PROTOCOL_HANDLER_PACKAGES = "java.protocol.handler.pkgs";private static final String PACKAGE = Handlers.class.getPackageName();private Handlers() {}public static void register() {// 獲取系統屬性java.protocol.handler.pkgs,該屬性用于指定協議處理器的包名String packages = System.getProperty("java.protocol.handler.pkgs", "");// 如果當前包名未包含在屬性中,則將其追加到屬性值中(以|分隔)packages = !packages.isEmpty() && !packages.contains(PACKAGE) ? packages + "|" + PACKAGE : PACKAGE;System.setProperty("java.protocol.handler.pkgs", packages);// 清除URL流處理器緩存resetCachedUrlHandlers();}private static void resetCachedUrlHandlers() {try {// 強制JVM重新加載URL流處理器URL.setURLStreamHandlerFactory((URLStreamHandlerFactory)null);} catch (Error var1) {}}
}

getClassPathUrls 方法

分析 ClassLoader classLoader = this.createClassLoader((Collection)this.getClassPathUrls()); 中的 (Collection)this.getClassPathUrls() 方法,調用 getClassPathUrls 方法返回值作為參數,該方法為抽象方法,具體實現在 ExecutableArchiveLauncher

public abstract class ExecutableArchiveLauncher extends Launcher {protected Set<URL> getClassPathUrls() throws Exception {return this.archive.getClassPathUrls(this::isIncludedOnClassPathAndNotIndexed, this::isSearchedDirectory);}
}

ExecutableArchiveLaunchergetClassPathUrls 方法執行 Archive 接口定義的 getClassPathUrls 方法返回的是包含所有匹配 URL 的有序集合

class JarFileArchive implements Archive {// 通過流處理遍歷JAR條目,應用過濾器篩選后轉換為URLpublic Set<URL> getClassPathUrls(Predicate<Archive.Entry> includeFilter, Predicate<Archive.Entry> directorySearchFilter) throws IOException {return (Set)this.jarFile.stream().map(JarArchiveEntry::new).filter(includeFilter).map(this::getNestedJarUrl).collect(Collectors.toCollection(LinkedHashSet::new));}// 根據條目注釋判斷是否為解壓存儲的嵌套JAR,若是則調用特殊處理方法,否則直接創建標準URL// archiveEntry:BOOT-INF/classes/private URL getNestedJarUrl(JarArchiveEntry archiveEntry) {try {JarEntry jarEntry = archiveEntry.jarEntry();String comment = jarEntry.getComment();return comment != null && comment.startsWith("UNPACK:") ? this.getUnpackedNestedJarUrl(jarEntry) : JarUrl.create(this.file, jarEntry);} catch (IOException var4) {IOException ex = var4;throw new UncheckedIOException(ex);}}
}

createClassLoader 方法

分析 ClassLoader classLoader = this.createClassLoader((Collection)this.getClassPathUrls()); 中的 createClassLoader 方法

LaunchedClassLoader 是 SpringBoot 自定義的類加載器,位于 org.springframework.boot.loader.LaunchedURLClassLoader, 專門用于加載 Spring Boot 可執行 JAR(即“胖 JAR”)中嵌套的依賴和資源。它的核心作用是解決傳統 Java 類加載器無法直接加載 JAR 內嵌 JAR(如 BOOT-INF/lib/ 中的依賴)的問題。且LaunchedClassLoader 在加載類時,會先嘗試自己加載(從嵌套 JAR 或用戶代碼),若找不到再委派父類加載器。這是對傳統雙親委派機制的擴展,確保優先加載應用自身的類。

public abstract class Launcher {protected ClassLoader createClassLoader(Collection<URL> urls) throws Exception {return this.createClassLoader((URL[])urls.toArray(new URL[0]));}private ClassLoader createClassLoader(URL[] urls) {ClassLoader parent = this.getClass().getClassLoader();return new LaunchedClassLoader(this.isExploded(), this.getArchive(), urls, parent);}
}

時序圖

+-----------------+     +--------------+     +----------------------+     +-------------------+
|      JVM        |     | JarLauncher  |     | LaunchedURLClassLoader|     | MainMethodRunner  |
+-----------------+     +--------------+     +----------------------+     +-------------------+|                     |                         |                           || 執行 java -jar app.jar |                       |                           ||--------------------->|                         |                           ||                     | 創建 Archive 對象        |                           ||                     |------------------------>|                           ||                     | 解析 MANIFEST.MF         |                           ||                     |<------------------------|                           ||                     | 調用 launch()            |                           ||                     |------------------------>|                           ||                     |                         | 創建類加載器              ||                     |                         |<--------------------------||                     |                         | 加載 BOOT-INF/classes/lib||                     |                         |-------------------------->||                     |                         |                           | 反射加載 Start-Class|                     |                         |                           |<------------------||                     |                         |                           | 調用 main()|                     |                         |                           |------------------>||                     |                         |                           | 執行用戶代碼       ||<------------------------------------------------------------(結果或異常)|| JVM 退出            |                         |                           ||<--------------------|                         |                           |

參考

  • 一文搞懂 Spring Boot 中 java -jar 的啟動 jar 包的原理_springboot java -jar-CSDN 博客
  • 芋道 Spring Boot Jar 啟動原理 | 芋道源碼 —— 純源碼解析博客
  • Site Unreachable

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

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

相關文章

YOLO11解決方案之速度估算探索

概述 Ultralytics提供了一系列的解決方案&#xff0c;利用YOLO11解決現實世界的問題&#xff0c;包括物體計數、模糊處理、熱力圖、安防系統、速度估計、物體追蹤等多個方面的應用。 YOLO速度估算結合物體檢測和跟蹤技術&#xff0c;使用YOLO11 模型檢測每幀中的物體&#xf…

初識C++:模版

本篇博客主要講解C模版的相關內容。 目錄 1.泛型編程 2.函數模板 2.1 函數模版概念 2.2 函數模版格式 2.3 函數模版的原理 2.4 函數模版的實例化 1.隱式實例化&#xff1a;讓編譯器根據實參推演模板參數的實際類型 2. 顯式實例化&#xff1a;在函數名后的<>中指定模…

人工智能100問?第27問:神經網絡與貝葉斯網絡的關系?

神經網絡與貝葉斯網絡是兩種互補的智能模型:神經網絡通過多層非線性變換從數據中學習復雜模式,擅長大規模特征提取和預測,而貝葉斯網絡基于概率推理建模變量間的條件依賴關系,擅長處理不確定性和因果推斷。兩者的融合(如貝葉斯神經網絡)結合了深度學習的表征能力與概率建…

【node.js】入門基礎

個人主頁&#xff1a;Guiat 歸屬專欄&#xff1a;node.js 文章目錄 1. Node.js簡介1.1 Node.js的核心特點1.2 Node.js適用場景 2. 第一個Node.js程序2.1 創建并運行Hello World2.2 創建簡單的HTTP服務器 3. Node.js核心概念3.1 模塊系統3.1.1 創建和導出模塊3.1.2 導入和使用模…

百度飛槳PaddleOCR 3.0開源發布 OCR精度躍升13%

百度飛槳 PaddleOCR 3.0 開源發布 2025 年 5 月 20 日&#xff0c;百度飛槳團隊正式發布了 PaddleOCR 3.0 版本&#xff0c;并將其開源。這一新版本在文字識別精度、多語種支持、手寫體識別以及高精度文檔解析等方面取得了顯著進展&#xff0c;進一步提升了 PaddleOCR 在 OCR …

Android 14 Binderized HAL開發實戰指南(AIDL版)

Android 14 Binderized HAL開發實戰指南&#xff08;AIDL版&#xff09; 環境要求 Android 14源碼編譯環境AOSP android-14.0.0_r7分支Soong build系統Java 17 & NDK r25c 項目結構 hardware/interfaces/myservice/ ├── 1.0 │ ├── IMyHalService.aidl # AID…

第九天的嘗試

目錄 一、每日一言 二、練習題 三、效果展示 四、下次題目 五、總結 一、每日一言 創造美好的代價是努力&#xff0c;失望以及毅力&#xff0c;首先是痛苦&#xff0c;然后才是歡樂。 時間是快的&#xff0c;看怎么利用&#xff0c;安排好一切事情&#xff0c;才能從容面對…

交安安全員:交通工程安全領域的關鍵角色

在交通工程這個龐大而復雜的領域中&#xff0c;交安安全員扮演著舉足輕重的角色&#xff0c;他們是安全的捍衛者&#xff0c;是交通工程順利推進的重要保障。? 交安安全員&#xff0c;專門從事公路水運工程施工企業安全生產管理工作。他們的專業身份由交通運輸部門頒發的交安…

實驗-設計一個應用系統(計算機組成原理)

目錄 一. 實驗內容 二. 實驗步驟 &#xff08;1&#xff09;七段數碼管顯示模塊 &#xff08;2&#xff09;指令模塊 &#xff08;3&#xff09;控制模塊 &#xff08;4&#xff09;ALU模塊 &#xff08;5&#xff09;CPU模塊 三. 實現效果 四. 實驗環境 五. 實驗小結…

【博客系統】博客系統第四彈:令牌技術

令牌機制 為什么不能使用 Session 實現登錄功能&#xff1f; 傳統思路&#xff1a; 登錄頁面把用戶名密碼提交給服務器。服務器端驗證用戶名密碼是否正確&#xff0c;并返回校驗結果給前端。如果密碼正確&#xff0c;則在服務器端創建 Session。通過 Cookie 把 sessionId 返回…

【瑞數3代】藥監評審中心逆向分析 | 后綴MmEwMD參數

1.目標 目標網址&#xff1a;https://www.cde.org.cn/main/news/listpage/545cf855a50574699b46b26bcb165f32 import requestscookies {FSSBBIl1UgzbN7N80S: 8sYeMWaC_IHoNl8Ckfx2y9MLiueMCkPr2V3MIoZkrMPUfzMMaXKzAoxpNPvyw4lt,Path: /,FSSBBIl1UgzbN7N80T: 3js3ygV.St6BvO20…

【漫話機器學習系列】274.基尼指數(Gini Index)

決策樹中的基尼指數&#xff08;Gini Index&#xff09;詳解 —— 從公式理解到實際應用 在構建決策樹模型時&#xff0c;一個核心問題是&#xff1a;如何選擇最優的特征來進行節點劃分&#xff1f; 這就涉及到了“劃分準則”的問題。常見的準則有信息增益、信息增益率以及本文…

R語言學習--Day07--T分布與T檢驗

昨天我們介紹了R中用于對數據進行分類的聚類分析的方法&#xff0c;接下來我們來看T分布。 T分布 T分布適用于幫我們估計整組數據&#xff08;較小的數據量&#xff0c;一般小于30&#xff09;的真實值在哪一個區間&#xff0c;具體是計算置信區間&#xff08;一般為95%&#…

數據結構與算法-線性表-雙向鏈表(Double Linked List)

1 線性表 1.4 雙向鏈表&#xff08;Double Linked List&#xff09; 雙向鏈表的結點中有兩個指針域&#xff0c;一個指向直接后繼&#xff0c;另一個指向直接前驅&#xff0c;主要是為了解決前向查找的問題。 雙向鏈表結構&#xff1a; 書籍和視頻教程都只講解了插入和刪除的…

甘特圖實例 dhtmlxGantt.js

本文介紹了如何使用dhtmlxGantt庫創建一個基礎的甘特圖示例&#xff0c;并對其進行漢化和自定義配置。首先&#xff0c;通過引入dhtmlxgantt.css和dhtmlxgantt.js文件初始化甘特圖。接著&#xff0c;通過設置gantt.i18n.setLocale("cn")實現核心文本的漢化&#xff0…

C++23 新增扁平化關聯容器詳解

文章目錄 一、引言已有關聯容器回顧新容器的引入原因 二、std::flat_set定義與特性代碼示例適用場景 三、std::flat_multiset定義與特性代碼示例適用場景 四、std::flat_map定義與特性代碼示例適用場景 五、std::flat_multimap定義與特性代碼示例適用場景 六、與其他容器的比較…

使用zap,對web應用/API接口 做安全檢測

https://www.zaproxy.org/getting-started/ 檢測方法 docker pull ghcr.io/zaproxy/zaproxy:stable# 執行baseline測試 docker run -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \ -t https://baseline.yeshen.org# 執行api測試 docker run -t ghcr.io/zaproxy/zaproxy…

Qt—模態與非模態對話框

Qt—模態與非模態對話框 核心概念 ?模態對話框??&#xff1a;強制用戶優先處理當前窗口&#xff0c;阻塞指定范圍的用戶交互。?非模態對話框??&#xff1a;允許用戶自由切換窗口&#xff0c;無交互限制。 一、模態對話框類型與行為 1. 應用級模態&#xff08;Applica…

Axure高保真CRM客戶關系管理系統原型

一套出色的CRM&#xff08;客戶關系管理&#xff09;系統&#xff0c;無疑是企業管理者掌控客戶動態、提升銷售業績的得力助手。今天&#xff0c;就為大家介紹一款精心打造的Axure高保真CRM客戶關系管理系統原型模板&#xff0c;助你輕松開啟高效客戶管理之旅。 這款CRM原型模…

【羊圈——狀壓 + DP / 記憶化搜索DP】

題目 一般DP代碼&#xff08;注意&#xff0c;這里只能向外推(起始狀態是f(1,0)&#xff0c;不能向內推&#xff08;不然會導致之前的羊圈被割裂&#xff09;&#xff09; #include <bits/stdc.h> using namespace std;const int MAX_N 210; const int MAX_M 16;int n…