一.理論
在spring中經常有讀取配置文件的需求,這里就會用到一個Spring提供的Resource接口
Resource 接口是具體資源訪問策略的抽象,也是所有資源訪問類所實現的接口。Resource 接口主要提供了如下幾個方法:
- getInputStream():定位並打開資源,返回資源對應的輸入流。每次調用都返回新的輸入流。調用者必須負責關閉輸入流。
- exists():返回 Resource 所指向的資源是否存在。
- isOpen():返回資源文件是否打開,如果資源文件不能多次讀取,每次讀取結束應該顯式關閉,以防止資源泄漏。
- getDescription():返回資源的描述信息,通常用於資源處理出錯時輸出該信息,通常是全限定文件名或實際 URL。
- getFile:返回資源對應的 File 對象。
- getURL:返回資源對應的 URL 對象。
Resource 接口本身沒有提供訪問任何底層資源的實現邏輯,針對不同的底層資源,Spring 將會提供不同的 Resource 實現類,不同的實現類負責不同的資源訪問邏輯。
Resource 和策略模式 Resource 接口就是策略模式的典型應用,Resource 接口就代表資源訪問策略,但具體采用哪種策略實現,Resource 接口並不理會。客戶端程序只和 Resource 接口耦合,並不知道底層采用何種資源訪問策略,這樣應用可以在不同的資源訪問策略之間自由切換。
Spring 為 Resource 接口提供了如下實現類:
- UrlResource:訪問網絡資源的實現類。
- ClassPathResource:訪問類加載路徑里資源的實現類。
- FileSystemResource:訪問文件系統里資源的實現類。
- ServletContextResource:訪問相對於 ServletContext 路徑里的資源的實現類:
- InputStreamResource:訪問輸入流資源的實現類。
- ByteArrayResource:訪問字節數組資源的實現類。
這些 Resource 實現類,針對不同的的底層資源,提供了相應的資源訪問邏輯,並提供便捷的包裝,以利於客戶端程序的資源訪問。
二.源碼
這里跟蹤ClassPathXmlApplicationContext對象的創建,來了解Spring底層怎么使用Resource接口及其實現類達成策略模式
策略模式的優勢 當 Spring 應用需要進行資源訪問時,實際上並不需要直接使用 Resource 實現類,而是調用 ApplicationContext 實例的 getResource() 方法來獲得資源,ApplicationContext 將會負責選擇 Resource 的實現類,也就是確定具體的資源訪問策略,從而將應用程序和具體的資源訪問策略分離開來,這就體現了策略模式的優勢。
1 //1.根據傳入的配置文件地址,創建容器對象 ClassPathXmlApplicationContext 2 public ClassPathXmlApplicationContext(String configLocation) throws BeansException { 3 this(new String[] {configLocation}, true, null); 4 }
1 //2.1中代碼調用到此處,此處調用父類構造函數 ClassPathXmlApplicationContext 2 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 3 throws BeansException { 4 5 super(parent); 6 setConfigLocations(configLocations); 7 if (refresh) { 8 refresh(); 9 } 10 }
1 //3.2中代碼一步步調用到此處 AbstractApplicationContext 2 public AbstractApplicationContext(ApplicationContext parent) { 3 this(); 4 setParent(parent); 5 }
1 //4.3中代碼調用此處 AbstractApplicationContext 2 public AbstractApplicationContext() { 3 this.resourcePatternResolver = getResourcePatternResolver(); 4 }
1 //5.返回能將1中傳入的configLocation轉換成Resource對象的ResourcePatternResolver AbstractApplicationContext 2 protected ResourcePatternResolver getResourcePatternResolver() { 3 return new PathMatchingResourcePatternResolver(this); 4 }
1 //6.真正用到策略模式的地方,根據location的內容,決定返回對應的Resource實現,這個過程對客戶來說是透明的 DefaultResourceLoader 2 @Override 3 public Resource getResource(String location) { 4 Assert.notNull(location, "Location must not be null"); 5 6 for (ProtocolResolver protocolResolver : this.protocolResolvers) { 7 Resource resource = protocolResolver.resolve(location, this); 8 if (resource != null) { 9 return resource; 10 } 11 } 12 13 if (location.startsWith("/")) { 14 return getResourceByPath(location); 15 } 16 else if (location.startsWith(CLASSPATH_URL_PREFIX)) { 17 return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); 18 } 19 else { 20 try { 21 // Try to parse the location as a URL... 22 URL url = new URL(location); 23 return new UrlResource(url); 24 } 25 catch (MalformedURLException ex) { 26 // No URL -> resolve as resource path. 27 return getResourceByPath(location); 28 } 29 } 30 }
參考:https://www.ibm.com/developerworks/cn/java/j-lo-spring-resource/