iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Software Development

30 篇淺談 Spring 框架的核心底層組件 系列 第 9

Day 09: ResourceLoader —— 資源的載入存取與路徑解析

  • 分享至 

  • xImage
  •  

到昨天為止,我們大致了解 Spring 是怎麼把一份 Java 程式轉變成一個可以被容器管理的 Bean。從今天開始,我們會在 Application 這個高度上停留一陣子,先不往其他地方走,目標是把容器的其餘 4 個功能講完;等容器的內容差不多完整了,再慢慢往外擴充到其他套件,講講 AOP、Transactional,最後再進到 Spring MVC 和 Spring Boot。

  • Resource

在談 Resource-Loader 之前,我們可以先簡單說說另一個 Resource 介面。Resource 本身繼承自 Input-Stream-Source,它是一個只定義了 getInputStream() 的核心介面,具體的功能是「把底層資料以串流的形式吐出來」;而 Resource 在此基礎上,多疊加了一層資源描述的能力,定義了 exists, isReadable, getURL, getFile, contentLength ...等方法。

而 Resource 的實體類別中,常見的有 Class-Path-Resource, File-System-Resource, Url-Resource, Byte-Array-Resource, 或 Web 環境下的 Servlet-Context-Resource ...等。雖然這幾個類別底層的實作邏輯相差甚遠,但因為都實作了同一個 Resource,統一了基本資訊的 IO 方法,使得後續拿到 Resource 物件的呼叫端可以不用知道手上這份資源究竟是怎麼實現的。算是一種簡化邏輯。

https://ithelp.ithome.com.tw/upload/images/20260923/20184114IxO7dIbReS.png

  • ResourceLoader

而 Resource-Loader 本身是一個底層介面,內部多定義了 2 個方法:getResource() 負責資源的解析與多型封裝,讓外部的呼叫端(例如 Application-Context 的其他組件)只要傳入一組路徑字串就能拿到預期的 Resource 物件,不需要知道資源真正放在哪。

getClassLoader() 則是 Class-Loader 的統一存取入口,當 Java 環境中同時存在多個 Class-Loader,各個組件都能透過這個方法取得同一個 classLoader,避免 Spring 在解析設定檔、掃描 @Component 或動態載入類別的時候用到了不同的 ClassLoader,使得系統後續出現問題。

https://ithelp.ithome.com.tw/upload/images/20260923/20184114KCvonN64aD.png

此外,從繼承鏈的角度出發,Application-Context 繼承的其實是一個 Resource-Loader 的衍生介面 —— Resource-Pattern-Resolver,它對原本的 Resource-Loader 做了一層封裝,主要新增了回傳 Resource[] 的 getResources 方法多型,用來支援 Ant 風格的萬用字元(例如 */*.xml*Mapper.xml)以及 classpath*: 前綴,把原始介面「一次取得一個資源」的能力延伸成「一次批量檢索多個資源」。

https://ithelp.ithome.com.tw/upload/images/20260923/20184114grI3gqFoJA.png

https://ithelp.ithome.com.tw/upload/images/20260923/20184114mRO7f4wQbg.png

  • ApplicationContext

然後是實務上的 IoC 容器。容器無論是哪一種 Application-Context,它們的共同父類都是 Abstract-Application-Context(後面簡稱 Abstract Context)。如果我們仔細一點看 Abstract Context 的繼承跟實作,就可以看到它除了實作 ApplicationContext 的子介面以外,也一併繼承了 Default-Resource-Loader。

https://ithelp.ithome.com.tw/upload/images/20260923/20184114gKsgGC2PNQ.png

Default-Resource-Loader 是 Resource-Loader 介面的預設核心實作類別,內部實現了單一資源定位與 ClassLoader 的統一管理機制。結合了這兩項能力後,容器在實際運作上採用了清晰的委派機制:如果路徑參數給定的是精確的字串,解析工作會直接交由父類 Default-Resource-Loader 的 getResource 方法,依前綴(classpath:、file:、http:)封裝成對應的 Resource;

而如果輸入中帶有萬用字元或 classpath*:,容器則委派給 Resource-Pattern-Resolver 的實作類別處理,批次回傳符合條件的 Resource[]。正是靠著這套分工,Spring 才能以統一、乾淨的介面,應付後續的設定檔載入、@ComponentScan 類別掃描與第三方框架的資源整合。

  • classpath

最後,簡單說一下 classpath:classpath*: 的差異:因為底層實作的原因,classpath: 的解析邏輯最終會落到 getResource(),這個方法只要在 classpath 的搜尋路徑找到第一個符合的檔案,就會直接回傳;但如果專案是多模組(or 多個依賴 jar)被一起打包的系統架構,同時好幾個 jar 底下都各自放了一份路徑相同的資源檔(例如多模組各自的 mapper.xml),用 classpath: 就只會拿到其中一份,漏掉其他 XML。

相對地,classpath*: 底層的呼叫則是多了一個 s 的 getResources(),這個方法會把 classpath 底下所有 classloader 根路徑都掃過一輪,只要路徑符合的資源都會被收集進最後回傳的 Resource[] 裡。進而解決剛才的 XML 問題,優點是不會找漏,但缺點就是如果專案不小,在啟動時就會有感變慢。


上一篇
Day 08: ApplicationContext —— 容器啟動的核心樞紐與流程解析
系列文
30 篇淺談 Spring 框架的核心底層組件 9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言