溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring Resource和策略模式如何應(yīng)用

發(fā)布時間:2022-10-18 15:55:37 來源:億速云 閱讀:100 作者:iii 欄目:編程語言

這篇文章主要介紹了Spring Resource和策略模式如何應(yīng)用的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Spring Resource和策略模式如何應(yīng)用文章都會有所收獲,下面我們一起來看看吧。

Spring 把所有能記錄信息的載體,如各種類型的文件、二進(jìn)制流等都稱為資源,對 Spring 開發(fā)者來說,最常用的資源就是 Spring 配置文件(通常是一份 XML 格式的文件)。

在 Sun 所提供的標(biāo)準(zhǔn) API 里,資源訪問通常由 java.net.URL 和文件 IO 來完成,尤其是當(dāng)我們需要訪問來自網(wǎng)絡(luò)的資源時,通常會選擇 URL 類。

URL 類可以處理一些常規(guī)的資源訪問問題,但依然不能很好地滿足所有底層資源訪問的需要,比如,暫時還無法從類加載路徑、或相對于 ServletContext 的路徑來訪問資源,雖然 Java 允許使用特定的 URL 前綴注冊新的處理類(例如已有的 http: 前綴的處理類),但是這樣做通常比較復(fù)雜,而且 URL 接口還缺少一些有用的功能,比如檢查所指向的資源是否存在等。

Spring 改進(jìn)了 Java 資源訪問的策略。Spring 為資源訪問提供了一個 Resource 接口,該接口提供了更強(qiáng)的資源訪問能力,Spring 框架本身大量使用了 Resource 接口來訪問底層資源。

Resource 接口是具體資源訪問策略的抽象,也是所有資源訪問類所實(shí)現(xiàn)的接口。Resource 接口主要提供了如下幾個方法:

  • getInputStream ():定位并打開資源,返回資源對應(yīng)的輸入流。每次調(diào)用都返回新的輸入流。調(diào)用者必須負(fù)責(zé)關(guān)閉輸入流。

  • exists ():返回 Resource 所指向的資源是否存在。

  • isOpen ():返回資源文件是否打開,如果資源文件不能多次讀取,每次讀取結(jié)束應(yīng)該顯式關(guān)閉,以防止資源泄漏。

  • getDescription ():返回資源的描述信息,通常用于資源處理出錯時輸出該信息,通常是全限定文件名或?qū)嶋H URL。

  • getFile:返回資源對應(yīng)的 File 對象。

  • getURL:返回資源對應(yīng)的 URL 對象。

Resource 和策略模式 Resource 接口就是策略模式的典型應(yīng)用,Resource 接口就代表資源訪問策略,但具體采用哪種策略實(shí)現(xiàn),Resource 接口并不理會??蛻舳顺绦蛑缓?Resource 接口耦合,并不知道底層采用何種資源訪問策略,這樣應(yīng)用可以在不同的資源訪問策略之間自由切換。

最后兩個方法通常無須使用,僅在通過簡單方式訪問無法實(shí)現(xiàn)時,Resource 提供傳統(tǒng)的資源訪問的功能。

Resource 接口本身沒有提供訪問任何底層資源的實(shí)現(xiàn)邏輯,針對不同的底層資源,Spring 將會提供不同的 Resource 實(shí)現(xiàn)類,不同的實(shí)現(xiàn)類負(fù)責(zé)不同的資源訪問邏輯。

Resource 不僅可在 Spring 的項(xiàng)目中使用,也可直接作為資源訪問的工具類使用。意思是說:即使不使用 Spring 框架,也可以使用 Resource 作為工具類,用來代替 URL。當(dāng)然,使用 Resource 接口會讓代碼與 Spring 的接口耦合在一起,但這種耦合只是部分工具集的耦合,不會造成太大的代碼污染。

Resource 的實(shí)現(xiàn)類

Resource 接口是 Spring 資源訪問策略的抽象,它本身并不提供任何資源訪問實(shí)現(xiàn),具體的資源訪問由該接口的實(shí)現(xiàn)類完成 —— 每個實(shí)現(xiàn)類代表一種資源訪問策略。

Spring 為 Resource 接口提供了如下實(shí)現(xiàn)類:

  • UrlResource:訪問網(wǎng)絡(luò)資源的實(shí)現(xiàn)類。

  • ClassPathResource:訪問類加載路徑里資源的實(shí)現(xiàn)類。

  • FileSystemResource:訪問文件系統(tǒng)里資源的實(shí)現(xiàn)類。

  • ServletContextResource:訪問相對于 ServletContext 路徑里的資源的實(shí)現(xiàn)類:

  • InputStreamResource:訪問輸入流資源的實(shí)現(xiàn)類。

  • ByteArrayResource:訪問字節(jié)數(shù)組資源的實(shí)現(xiàn)類。

這些 Resource 實(shí)現(xiàn)類,針對不同的的底層資源,提供了相應(yīng)的資源訪問邏輯,并提供便捷的包裝,以利于客戶端程序的資源訪問。

使用 UrlResource 訪問網(wǎng)絡(luò)資源

訪問網(wǎng)絡(luò)資源通過 UrlResource 類實(shí)現(xiàn),UrlResource 是 java.net.URL 類的包裝,主要用于訪問之前通過 URL 類訪問的資源對象。URL 資源通常應(yīng)該提供標(biāo)準(zhǔn)的協(xié)議前綴。例如:file: 用于訪問文件系統(tǒng);http: 用于通過 HTTP 協(xié)議訪問資源;ftp: 用于通過 FTP 協(xié)議訪問資源等。

UrlResource 類實(shí)現(xiàn) Resource 接口,對 Resource 全部方法提供了實(shí)現(xiàn),完全支持 Resource 的全部 API。下面代碼示范了使用 UrlResource 訪問文件系統(tǒng)資源的示例。程序如下:

清單 1. UrlResourceTest.java
public class UrlResourceTest 
{ 
public static void main(String[] args) throws Exception 
{ 
// 創(chuàng)建一個 Resource 對象,指定從文件系統(tǒng)里讀取資源
UrlResource ur = new UrlResource("file:book.xml");
// 獲取該資源的簡單信息
System.out.println(ur.getFilename()); 
System.out.println(ur.getDescription()); 
// 創(chuàng)建 Dom4j 的解析器
SAXReader reader = new SAXReader(); 
Document doc = reader.read(ur.getFile()); 
// 獲取根元素
Element el = doc.getRootElement(); 
List l = el.elements(); 
// 此處省略了訪問、輸出 XML 文檔內(nèi)容的代碼。
... 
} 
}

上面程序中粗體字代碼使用 UrlResource 來訪問本地磁盤資源,雖然 UrlResource 是為訪問網(wǎng)絡(luò)資源而設(shè)計的,但通過使用 file 前綴也可訪問本地磁盤資源。如果需要訪問網(wǎng)絡(luò)資源,可以使用如下兩個常用前綴:

  • http:-該前綴用于訪問基于 HTTP 協(xié)議的網(wǎng)絡(luò)資源。

  • ftp:-該前綴用于訪問基于 FTP 協(xié)議的網(wǎng)絡(luò)資源。

由于 UrlResource 是對 java.net.URL 的封裝,所以 UrlResource 支持的前綴與 URL 類所支持的前綴完全相同。

將應(yīng)用所需的 book.xml 訪問放在應(yīng)用的當(dāng)前路徑,運(yùn)行該程序,即可看到使用 UrlResource 訪問本地磁盤資源的效果。

使用 ClassPathResource 訪問類加載路徑下的資源。

ClassPathResource 用來訪問類加載路徑下的資源,相對于其他的 Resource 實(shí)現(xiàn)類,其主要優(yōu)勢是方便訪問類加載路徑里的資源,尤其對于 Web 應(yīng)用,ClassPathResource 可自動搜索位于 WEB-INF/classes 下的資源文件,無須使用絕對路徑訪問。

下面示例程序示范了將 book.xml 放在類加載路徑下,然后使用如下程序訪問它:

清單 2. ClassPathResourceTest.java
public class ClassPathResourceTest 
{ 
public static void main(String[] args) throws Exception 
{ 
      // 創(chuàng)建一個 Resource 對象,從類加載路徑里讀取資源
      ClassPathResource cr = new ClassPathResource("book.xml");
      // 獲取該資源的簡單信息
      System.out.println(cr.getFilename()); 
      System.out.println(cr.getDescription()); 
      // 創(chuàng)建 Dom4j 的解析器
      SAXReader reader = new SAXReader(); 
      Document doc = reader.read(cr.getFile()); 
      // 獲取根元素
      Element el = doc.getRootElement(); 
      List l = el.elements(); 
      // 此處省略了訪問、輸出 XML 文檔內(nèi)容的代碼。
      ... 
} 
}

上面程序的粗體字代碼用于訪問類加載路徑下的 book.xml 文件,對比前面進(jìn)行資源訪問的 2 個示例程序,我們發(fā)現(xiàn)兩個程序除了進(jìn)行資源訪問的代碼有所區(qū)別之外,其他程序代碼基本一致,這就是 Spring 資源訪問的優(yōu)勢:Spring 的資源訪問消除了底層資源訪問的差異,允許程序以一致的方式來訪問不同的底層資源。

ClassPathResource 實(shí)例可使用 ClassPathResource 構(gòu)造器顯式地創(chuàng)建,但更多的時候它都是隱式創(chuàng)建的,當(dāng)執(zhí)行 Spring 的某個方法時,該方法接受一個代表資源路徑的字符串參數(shù),當(dāng) Spring 識別該字符串參數(shù)中包含 classpath: 前綴后,系統(tǒng)將會自動創(chuàng)建 ClassPathResource 對象。

使用 FileSystemResource 訪問文件系統(tǒng)資源

Spring 提供的 FileSystemResource 類用于訪問文件系統(tǒng)資源,使用 FileSystemResource 來訪問文件系統(tǒng)資源并沒有太大的優(yōu)勢,因?yàn)?Java 提供的 File 類也可用于訪問文件系統(tǒng)資源。

當(dāng)然使用 FileSystemResource 也可消除底層資源訪問的差異,程序通過統(tǒng)一的 Resource API 來進(jìn)行資源訪問。下面程序是使用 FileSystemResource 來訪問文件系統(tǒng)資源的示例程序。

清單 3. FileSystemResourceTest.java
public class FileSystemResourceTest 
{ 
public static void main(String[] args) throws Exception 
{ 
// 默認(rèn)從文件系統(tǒng)的當(dāng)前路徑加載 book.xml 資源
FileSystemResource fr = new FileSystemResource("book.xml"); 
// 獲取該資源的簡單信息
System.out.println(fr.getFilename()); 
System.out.println(fr.getDescription()); 
// 創(chuàng)建 Dom4j 的解析器
SAXReader reader = new SAXReader(); 
Document doc = reader.read(fr.getFile()); 
// 獲取根元素
Element el = doc.getRootElement(); 
List l = el.elements(); 
// 此處省略了訪問、輸出 XML 文檔內(nèi)容的代碼。
... 
} 
}

與前兩種 Resource 作資源訪問的區(qū)別在于:資源字符串確定的資源,位于本地文件系統(tǒng)內(nèi) ,而且無須使用任何前綴。

FileSystemResource 實(shí)例可使用 FileSystemResource 構(gòu)造器顯式地創(chuàng)建。但更多的時候它都是隱式創(chuàng)建的,執(zhí)行 Spring 的某個方法時,該方法接受一個代表資源路徑的字符串參數(shù),當(dāng) Spring 識別該字符串參數(shù)中包含 file: 前綴后,系統(tǒng)將會自動創(chuàng)建 FileSystemResource 對象。

通過上面代碼不難發(fā)現(xiàn),程序使用 UrlResource、FileSystemResource、ClassPathResource 三個實(shí)現(xiàn)類來訪問資源的代碼差異并不大,唯一的缺點(diǎn)在于客戶端代碼需要與 Resource 接口的實(shí)現(xiàn)類耦合,這依然無法實(shí)現(xiàn)高層次解耦。

這對于策略模式來說將沒有任何問題,策略模式要解決的就是這個問題,策略模式將會提供一個 Context 類來為客戶端代碼 “智能” 地選擇策略實(shí)現(xiàn)類。至此我們發(fā)現(xiàn)了 Spring 資源訪問的兩個重要部分:Resource 接口和多個實(shí)現(xiàn)類,它們之間有如圖 1 所示

圖 1.Spring 資源訪問的策略接口和策略實(shí)現(xiàn)類

圖 1 所示的類圖中提供了一個 Resouce 接口,這個接口就是 Spring 為資源訪問所提供的策略接口,該接口下的大量實(shí)現(xiàn)類:UrlResource、ClassPathResource、FileSystemResource、ServletContextResource、ByteArrayResource、InputStreamReource 都實(shí)現(xiàn)了該策略接口,用于實(shí)現(xiàn)不同的資源訪問策略。

下面我們將通過一個淺顯的示例來講解策略模式:

策略模式

策略模式用于封裝系列的算法,這些算法通常被封裝在一個被稱為 Context 類中,客戶端程序可以自由選擇其中一種算法,或讓 Context 為客戶端選擇一個最佳的算法 —— 使用策略模式的優(yōu)勢是為了支持算法的自由切換。

考慮如下場景:現(xiàn)在我們正在開發(fā)一個網(wǎng)上書店,該書店為了更好地促銷,經(jīng)常需要對圖書進(jìn)行打折促銷,程序需要考慮各種打折促銷的計算方法。

為了實(shí)現(xiàn)書店現(xiàn)在所提供的各種打折需求,程序考慮使用如下方式來實(shí)現(xiàn)

// 一段實(shí)現(xiàn) discount () 方法代碼

public double discount(double price) 
{ 
// 針對不同情況采用不同的打折算法
switch(getDiscountType()){case VIP_DISCOUNT:return vipDiscount(price);case OLD_DISCOUNT:return oldDiscount(price);case SALE_DISCOUNT:return saleDiscount(price);...}
}

上面粗體字代碼會根據(jù)打折類型來決定使用不同的打折算法,從而滿足該書店促銷打折的要求。從功能實(shí)現(xiàn)的角度來看,這段代碼沒有太大的問題。但這段代碼有一個明顯的不足,程序中各種打折方法都被直接寫入了 discount (double price) 方法中。如有一天,該書店需要新增一種打折類型呢?那開發(fā)人員必須修改至少三處代碼:首先需要增加一個常量,該常量代表新增的打折類型;其次需要在 switch 語句中增加一個 case 語句;最后開發(fā)人員需要實(shí)現(xiàn) xxxDiscount () 方法,用于實(shí)現(xiàn)新增的打折算法。

為了改變這種不好的設(shè)計,下面將會選擇使用策略模式來實(shí)現(xiàn)該功能,下面先提供一個打折算法的接口,該接口里包含一個 getDiscount () 方法,該接口代碼如下:

清單 4. DiscountStrategy.java
public interface DiscountStrategy 
{ 
//定義一個用于計算打折價的方法
double getDiscount(double originPrice); 
}

對于程序中這個 DiscountStrategy 接口而言,實(shí)現(xiàn)該接口的實(shí)現(xiàn)類就可以實(shí)現(xiàn)打折,但具體的打折策略與該接口無關(guān),而是由具體的實(shí)現(xiàn)類來決定打折策略。由此可見,這個 DiscountStrategy 接口的作用和 Spring 框架中 Resource 接口的作用完全相同。

就像 Spring 框架必須為 Resource 接口提供大量實(shí)現(xiàn)類一樣,我們此處也需要為 DiscountStrategy 接口提供兩個實(shí)現(xiàn)類,每個實(shí)現(xiàn)類代表一種打折策略。

下面代表 VIP 打折策略

清單 5. VipDiscount.java
// 實(shí)現(xiàn) DiscountStrategy 接口,實(shí)現(xiàn)對 VIP 打折的算法
public class VipDiscount 
implements DiscountStrategy 
{ 
// 重寫 getDiscount() 方法,提供 VIP 打折算法
public double getDiscount(double originPrice) 
{ 
System.out.println("使用 VIP 折扣 ..."); 
return originPrice * 0.5; 
} 
}

下面代表舊書打折策略

清單 6. OldDiscount.java
public class OldDiscount 
implements DiscountStrategy 
{ 
// 重寫 getDiscount() 方法,提供舊書打折算法
public double getDiscount(double originPrice) 
{ 
System.out.println("使用舊書折扣 ..."); 
return originPrice * 0.7; 
} 
}

此時遇到了與前面程序相同的問題,如果客戶端代碼直接與具體的策略類(如 VIPDiscount、OldDiscount)耦合,那客戶端代碼將無法實(shí)現(xiàn)解耦。因此策略模式需要為客戶端代碼提供了一個 Context 類,讓它為客戶端代碼決定采用哪種策略。例如本示例程序提供一個 DiscountContext 類,該類用于為客戶端代碼選擇合適折扣策略,當(dāng)然也允許用戶自由選擇折扣策略。下面是該 DiscountContext 類的代碼:

清單 7. DiscountContext.java
public class DiscountContext 
{ 
// 組合一個 DiscountStrategy 對象
private DiscountStrategy strategy; 
// 構(gòu)造器,傳入一個 DiscountStrategy 對象
public DiscountContext(DiscountStrategy strategy) 
{ 
this.strategy  = strategy; 
} 
// 根據(jù)實(shí)際所使用的 DiscountStrategy 對象得到折扣價
public double getDiscountPrice(double price) 
{ 
// 如果 strategy 為 null,系統(tǒng)自動選擇 OldDiscount 類
if (strategy == null){strategy = new OldDiscount();}return this.strategy.getDiscount(price);
} 
// 提供切換算法的方法
public void changeDiscount(DiscountStrategy strategy) 
{ 
this.strategy = strategy; 
} 
}

從上面程序的粗體字代碼可以看出,該 Context 類扮演了決策者的角色,它決定調(diào)用哪個折扣策略來處理圖書打折。當(dāng)客戶端代碼沒有選擇合適的折扣時,該 Context 會自動選擇 OldDiscount 折扣策略;用戶也可根據(jù)需要選擇合適的折扣策略。

下面程序示范了客戶端代碼使用該 Contex 類來處理圖書打折:

清單 8. StrategyTest.java
public class StrategyTest 
{ 
public static void main(String[] args) 
{ 
// 客戶端沒有選擇打折策略類
DiscountContext dc = new DiscountContext(null);
double price1 = 79; 
// 使用默認(rèn)的打折策略
System.out.println("79 元的書默認(rèn)打折后的價格是:" 
+ dc.getDiscountPrice(price1)); 
// 客戶端選擇合適的 VIP 打折策略
dc.changeDiscount(new VipDiscount());
double price2 = 89; 
// 使用 VIP 打折得到打折價格
System.out.println("89 元的書對 VIP 用戶的價格是:" 
+ dc.getDiscountPrice(price2)); 
} 
}

上面程序第一行粗體字代碼創(chuàng)建了一個 DiscountContext 對象,客戶端并未指定實(shí)際所需的打折策略類,故程序?qū)⑹褂媚J(rèn)的打折策略類;程序第二行粗體字代碼指定使用 VipDiscount 策略類,故程序?qū)⒏臑槭褂檬褂?VIP 打折策略。

再次考慮前面的需求:當(dāng)業(yè)務(wù)需要新增一種打折類型時,系統(tǒng)只需要新定義一個 DiscountStrategy 實(shí)現(xiàn)類,該實(shí)現(xiàn)類實(shí)現(xiàn) getDiscount () 方法,用于實(shí)現(xiàn)新的打折算法即可??蛻舳顺绦蛐枰袚Q為新的打折策略時,則需要先調(diào)用 DiscountContext 的 setDiscount () 方法切換為新的打折策略。

從上面介紹中可以看出,使用策略模式可以讓客戶端代碼在不同的打折策略之間切換,但也有一個小小的遺憾:客戶端代碼需要和不同的策略類耦合。

為了彌補(bǔ)這個不足,我們可以考慮使用配置文件來指定 DiscountContext 使用哪種打折策略 —— 這就徹底分離客戶端代碼和具體打折策略 —— 這正好是 Spring 框架的強(qiáng)項(xiàng),Spring 框架采用配置文件來管理 Bean,當(dāng)然也可以管理資源。

下面我們再回到 Spring 框架里,看看 Spring 框架的 Context 如何 “智能” 地選擇資源訪問策略,

ResourceLoader 接口和 ResourceLoaderAware 接口

Spring 提供兩個標(biāo)志性接口:

  • ResourceLoader:該接口實(shí)現(xiàn)類的實(shí)例可以獲得一個 Resource 實(shí)例。

  • ResourceLoaderAware:該接口實(shí)現(xiàn)類的實(shí)例將獲得一個 ResourceLoader 的引用。

在 ResourceLoader 接口里有如下方法:

  • Resource getResource (String location):該接口僅包含這個方法,該方法用于返回一個 Resource 實(shí)例。ApplicationContext 的實(shí)現(xiàn)類都實(shí)現(xiàn) ResourceLoader 接口,因此 ApplicationContext 可用于直接獲取 Resource 實(shí)例。

策略模式的優(yōu)勢 當(dāng) Spring 應(yīng)用需要進(jìn)行資源訪問時,實(shí)際上并不需要直接使用 Resource 實(shí)現(xiàn)類,而是調(diào)用 ApplicationContext 實(shí)例的 getResource () 方法來獲得資源,ApplicationContext 將會負(fù)責(zé)選擇 Resource 的實(shí)現(xiàn)類,也就是確定具體的資源訪問策略,從而將應(yīng)用程序和具體的資源訪問策略分離開來,這就體現(xiàn)了策略模式的優(yōu)勢。

此處 Spring 框架的 ApplicationContext 不僅是 Spring 容器,而且它還是資源訪問策略的 “決策者”,也就是策略模式中 Context 對象,它將為客戶端代碼 “智能” 地選擇策略實(shí)現(xiàn)。

當(dāng) ApplicationContext 實(shí)例獲取 Resource 實(shí)例時,系統(tǒng)將默認(rèn)采用與 ApplicationContext 相同的資源訪問策略。對于如下代碼:

// 通過 ApplicationContext 訪問資源

Resource res = ctx.getResource("some/resource/path/myTemplate.txt);

從上面代碼中無法確定 Spring 將哪個實(shí)現(xiàn)類來訪問指定資源,Spring 將采用和 ApplicationContext 相同的策略來訪問資源。也就是說:如果 ApplicationContext 是 FileSystemXmlApplicationContext,res 就是 FileSystemResource 實(shí)例;如果 ApplicationContext 是 ClassPathXmlApplicationContext,res 就是 ClassPathResource 實(shí)例;如果 ApplicationContext 是 XmlWebApplicationContext,res 是 ServletContextResource 實(shí)例。

看如下示例程序,下面程序?qū)⑹褂?ApplicationContext 來訪問資源:

清單 9. ResourceAwareTest.java
public class ResourceAwareTest 
{ 
public static void main(String[] args) throws Exception 
{ 
// 創(chuàng)建 ApplicationContext 實(shí)例
ApplicationContext ctx = newClassPathXmlApplicationContext("bean.xml");Resource res = ctx.getResource("book.xml");
// 獲取該資源的簡單信息
System.out.println(res.getFilename()); 
System.out.println(res.getDescription()); 
// 創(chuàng)建 Dom4j 的解析器
SAXReader reader = new SAXReader(); 
Document doc = reader.read(res.getFile()); 
// 獲取根元素
Element el = doc.getRootElement(); 
List l = el.elements(); 
// 此處省略了訪問、輸出 XML 文檔內(nèi)容的代碼。
... 
} 
}

上面程序中第一行粗體字創(chuàng)建了一個 ApplictionContext 對象,第二行粗體字代碼通過該對象來獲取資源,由于程序中使用了 ClassPathApplicationContext 來獲取資源,所以 Spring 將會從類加載路徑下來訪問資源,也就是使用 ClassPathResource 實(shí)現(xiàn)類。

另一方面使用 ApplicationContext 來訪問資源時,也可不理會 ApplicationContext 的實(shí)現(xiàn)類,強(qiáng)制使用指定的 ClassPathResource、FileSystemResource 等實(shí)現(xiàn)類,這可通過不同前綴來指定,如下代碼所示:

// 通過 classpath: 前綴,強(qiáng)制使用 ClassPathResource ``Resource r = ctx.getResource("classpath:bean.xml”);

類似地,還可以使用標(biāo)準(zhǔn)的 java.net.URL 前綴來強(qiáng)制使用 UrlResource,如下所示:

// 通過標(biāo)準(zhǔn) file: 前綴,強(qiáng)制使用 UrlResource 訪問本地文件資源``Resource r = ctx.getResource("file:bean.xml); ``// 通過標(biāo)準(zhǔn) http: 前綴,強(qiáng)制使用 UrlResource 基于 HTTP 協(xié)議的網(wǎng)絡(luò)資源``Resource r = ctx.getResource("http://localhost:8888/bean.xml);

以下是常見前綴及對應(yīng)的訪問策略:

  • classpath: 以 ClassPathResource 實(shí)例來訪問類路徑里的資源。

  • file: 以 UrlResource 實(shí)例訪問本地文件系統(tǒng)的資源。

  • http: 以 UrlResource 實(shí)例訪問基于 HTTP 協(xié)議的網(wǎng)絡(luò)資源。

  • 無前綴:由于 ApplicationContext 的實(shí)現(xiàn)類來決定訪問策略。

ResourceLoaderAware 接口則用于指定該接口的實(shí)現(xiàn)類必須持有一個 ResourceLoader 實(shí)例。

類似于 Spring 提供的 BeanFactoryAware、BeanNameAware 接口,ResourceLoaderAware 接口也提供了一個 setResourceLoader () 方法,該方法將由 Spring 容器負(fù)責(zé)調(diào)用,Spring 容器會將一個 ResourceLoader 對象作為該方法的參數(shù)傳入。

當(dāng)我們把將 ResourceLoaderAware 實(shí)例部署在 Spring 容器中后,Spring 容器會將自身當(dāng)成 ResourceLoader 作為 setResourceLoader () 方法的參數(shù)傳入,由于 ApplicationContext 的實(shí)現(xiàn)類都實(shí)現(xiàn)了 ResourceLoader 接口,Spring 容器自身完全可作為 ResourceLoader 使用。

使用 Resource 作為屬性

前面介紹了 Spring 提供的資源訪問策略,但這些依賴訪問策略要么需要使用 Resource 實(shí)現(xiàn)類,要么需要使用 ApplicationContext 來獲取資源。實(shí)際上,當(dāng)應(yīng)用程序中的 Bean 實(shí)例需要訪問資源時,Spring 有更好的解決方法:直接利用依賴注入。

從這個意義上來看,Spring 框架不僅充分利用了策略模式來簡化資源訪問,而且還將策略模式和 IoC 進(jìn)行充分地結(jié)合,最大程度地簡化了 Spring 資源訪問。

歸納起來,如果 Bean 實(shí)例需要訪問資源,有如下兩種解決方案:

  • 代碼中獲取 Resource 實(shí)例。

  • 使用依賴注入。

對于第一種方式的資源訪問,當(dāng)程序獲取 Resource 實(shí)例時,總需要提供 Resource 所在的位置,不管通過 FileSystemResource 創(chuàng)建實(shí)例,還是通過 ClassPathResource 創(chuàng)建實(shí)例,或者通過 ApplicationContext 的 getResource () 方法獲取實(shí)例,都需要提供資源位置。這意味著:資源所在的物理位置將被耦合到代碼中,如果資源位置發(fā)生改變,則必須改寫程序。因此,通常建議采用第二種方法,讓 Spring 為 Bean 實(shí)例依賴注入資源。

看如下 TestBean,它有一個 Resource 類型的 res Field,程序并為該 Field 提供了對應(yīng)的 setter 方法,這就可以利用 Spring 的依賴注入了。

清單 9. TestBean.java
public class TestBean 
{ 
  private Resource res;
  // 依賴注入 Resource 資源的 setter 方法
  public void setResource(Resource res){this.res = res;}
  public void parse()throws Exception 
  { 
      // 獲取該資源的簡單信息
      System.out.println(res.getFilename()); 
      System.out.println(res.getDescription()); 
      // 創(chuàng)建 Dom4j 的解析器
      SAXReader reader = new SAXReader(); 
      Document doc = reader.read(res.getFile()); 
      // 獲取根元素
      Element el = doc.getRootElement(); 
      List l = el.elements(); 
      // 此處省略了訪問、輸出 XML 文檔內(nèi)容的代碼。
      ... 
  } 
}

上面程序中粗體字代碼定義了一個 Resource 類型的 res 屬性,該屬性需要可以接受 Spring 的依賴注入。除此之外,程序中的 parse () 方法用于解析 res 資源所代表的 XML 文件。

在容器中配置該 Bean,并為該 Bean 指定資源文件的位置,配置文件如下:

清單 10. bean.xml
<?xml version="1.0" encoding="GBK"?> 
 <!-- 指定 Spring 配置文件的 DTD 信息 -->
 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd"> 
 <!-- Spring 配置文件的根元素 -->
 <beans> 
 <bean id="test" class="lee.TestBean"> 
 <!-- 注入資源 -->
 <property name="resource"value="classpath:book.xml"/>
 </bean> 
 </beans>

上面配置文件中粗體字代碼配置了資源的位置,并使用了 classpath: 前綴,這指明讓 Spring 從類加載路徑里加載 book.xml 文件。與前面類似的是,此處的前綴也可采用 http:、ftp: 等,這些前綴將強(qiáng)制 Spring 采用怎樣的資源訪問策略(也就是指定具體使用哪個 Resource 實(shí)現(xiàn)類);如果不采用任何前綴,則 Spring 將采用與該 ApplicationContext 相同的資源訪問策略來訪問資源。

采用依賴注入,允許動態(tài)配置資源文件位置,無須將資源文件位置寫在代碼中,當(dāng)資源文件位置發(fā)生變化時,無須改寫程序,直接修改配置文件即可。

關(guān)于“Spring Resource和策略模式如何應(yīng)用”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Spring Resource和策略模式如何應(yīng)用”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI