真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

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

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

創(chuàng)新互聯(lián)公司是一家專業(yè)提供青山企業(yè)網(wǎng)站建設(shè),專注與成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè)、H5網(wǎng)站設(shè)計(jì)、小程序制作等業(yè)務(wù)。10年已為青山眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡(luò)公司優(yōu)惠進(jìn)行中。

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

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

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

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

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

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

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

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

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

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

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

Resource 和策略模式Resource 接口就是策略模式的典型應(yīng)用,Resource 接口就代表資源訪問(wèn)策略,但具體采用哪種策略實(shí)現(xiàn),Resource 接口并不理會(huì)。客戶端程序只和 Resource 接口耦合,并不知道底層采用何種資源訪問(wèn)策略,這樣應(yīng)用可以在不同的資源訪問(wèn)策略之間自由切換。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

清單 1. UrlResourceTest.java
public class UrlResourceTest 
{ 
public static void main(String[] args) throws Exception 
{ 
// 創(chuàng)建一個(gè) Resource 對(duì)象,指定從文件系統(tǒng)里讀取資源
UrlResource ur = new UrlResource("file:book.xml");
// 獲取該資源的簡(jiǎn)單信息
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(); 
// 此處省略了訪問(wèn)、輸出 XML 文檔內(nèi)容的代碼。
... 
} 
}

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

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

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

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

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

使用 ClassPathResource 訪問(wèn)類加載路徑下的資源。

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

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

清單 2. ClassPathResourceTest.java
public class ClassPathResourceTest 
{ 
public static void main(String[] args) throws Exception 
{ 
      // 創(chuàng)建一個(gè) Resource 對(duì)象,從類加載路徑里讀取資源
      ClassPathResource cr = new ClassPathResource("book.xml");
      // 獲取該資源的簡(jiǎn)單信息
      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(); 
      // 此處省略了訪問(wèn)、輸出 XML 文檔內(nèi)容的代碼。
      ... 
} 
}

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

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

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

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

當(dāng)然使用 FileSystemResource 也可消除底層資源訪問(wèn)的差異,程序通過(guò)統(tǒng)一的 Resource API 來(lái)進(jìn)行資源訪問(wèn)。下面程序是使用 FileSystemResource 來(lái)訪問(wèn)文件系統(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"); 
// 獲取該資源的簡(jiǎn)單信息
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(); 
// 此處省略了訪問(wèn)、輸出 XML 文檔內(nèi)容的代碼。
... 
} 
}

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

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

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

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

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

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

下面我們將通過(guò)一個(gè)淺顯的示例來(lái)講解策略模式:

策略模式

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

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

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

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

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

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

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

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

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

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

下面代表 VIP 打折策略

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

下面代表舊書(shū)打折策略

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

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

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

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

下面程序示范了客戶端代碼使用該 Contex 類來(lái)處理圖書(shū)打折:

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

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

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

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

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

下面我們?cè)倩氐?Spring 框架里,看看 Spring 框架的 Context 如何 “智能” 地選擇資源訪問(wèn)策略,

ResourceLoader 接口和 ResourceLoaderAware 接口

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

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

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

在 ResourceLoader 接口里有如下方法:

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

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

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

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

// 通過(guò) ApplicationContext 訪問(wèn)資源

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

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

看如下示例程序,下面程序?qū)⑹褂?ApplicationContext 來(lái)訪問(wèn)資源:

清單 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");
// 獲取該資源的簡(jiǎn)單信息
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(); 
// 此處省略了訪問(wèn)、輸出 XML 文檔內(nèi)容的代碼。
... 
} 
}

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

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

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

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

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

以下是常見(jiàn)前綴及對(duì)應(yīng)的訪問(wèn)策略:

  • classpath: 以 ClassPathResource 實(shí)例來(lái)訪問(wèn)類路徑里的資源。

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

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

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

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

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

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

使用 Resource 作為屬性

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

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

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

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

  • 使用依賴注入。

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

看如下 TestBean,它有一個(gè) Resource 類型的 res Field,程序并為該 Field 提供了對(duì)應(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 
  { 
      // 獲取該資源的簡(jiǎn)單信息
      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(); 
      // 此處省略了訪問(wèn)、輸出 XML 文檔內(nèi)容的代碼。
      ... 
  } 
}

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

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

清單 10. bean.xml
 
 
  
 
  
  
 
 
  
 

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

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

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


本文題目:SpringResource和策略模式如何應(yīng)用
URL地址:http://weahome.cn/article/jjcsdh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部