1. 程式人生 > >Spring08-----IoC容器ApplicationContext

Spring08-----IoC容器ApplicationContext

文件系統 system 標記接口 apt filesyste 永遠 geturl end mxml

作為Spring提供的較之BeanFactory更為先進的IoC容器實現,ApplicationContext除了擁有 BeanFactory支持的所有功能之外,還進一步擴展了基本容器的功能,包括BeanFactoryPostProces- sor、BeanPostProcessor以及其他特殊類型bean的自動識別、容器啟動後bean實例的自動初始化、 國際化的信息支持、容器內事件發布等。真是“青出於藍而勝於藍”啊!

在介紹ApplicationContext之前先引入資源的概念。

一. Resource接口

在日常程序開發中,處理外部資源是很繁瑣的事情,我們可能需要處理URL資源、File資源資源、ClassPath相關 資源、服務器相關資源(JBoss AS 5.x上的VFS資源)等等很多資源。因此處理這些資源需要使用不同的接口,這就 增加了我們系統的復雜性;而且處理這些資源步驟都是類似的(打開資源、讀取資源、關閉資源),因此如果能抽象 出一個統一的接口來對這些底層資源進行統一訪問,是不是很方便,而且使我們系統更加簡潔,都是對不同的底層資 源使用同一個接口進行訪問。
Spring 提供一個Resource接口來統一這些底層資源一致的訪問

,而且提供了一些便利的接口,從而能提供我們 的生產力。

1. Resource接口API

Spring的Resource接口代表底層外部資源,提供了對底層外部資源的一致性訪問接口。

public interface InputStreamSource { 
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource { 
     boolean exists(); 
     boolean isReadable(); 
     boolean
isOpen(); URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription(); }

(1)InputStreamSource接口解析

getInputStream:每次調用都將返回一個新鮮的資源對應的java.io. InputStream字節流,調用者在使用完畢 後必須關閉該資源。

(2)Resource接口繼承InputStreamSource接口,並提供一些便利方法:

exists:返回當前Resource代表的底層資源是否存在,true表示存在。
isReadable:返回當前Resource代表的底層資源是否可讀,true表示可讀。
isOpen:返回當前Resource代表的底層資源是否已經打開,如果返回true,則只能被讀取一次然後關閉以避 免資源泄露;常見的Resource實現一般返回false。
getURL:如果當前Resource代表的底層資源能由java.util.URL代表,則返回該URL,否則拋出IOException。
getURI:如果當前Resource代表的底層資源能由java.util.URI代表,則返回該URI,否則拋出IOException。
getFile:如果當前Resource代表的底層資源能由java.io.File代表,則返回該File,否則拋出IOException。
contentLength:返回當前Resource代表的底層文件資源的長度,一般是值代表的文件資源的長度。
lastModified:返回當前Resource代表的底層資源的最後修改時間。
createRelative:用於創建相對於當前Resource代表的底層資源的資源,比如當前Resource代表文件資源 “d:/test/”則createRelative(“test.txt”)將返回表文件資源“d:/test/test.txt”Resource資源。
getFilename:返回當前Resource代表的底層文件資源的文件路徑,比如File資源“file://d:/test.txt”將返回 “d:/test.txt”,而URL資源http://www.javass.cn將返回“”,因為只返回文件路徑。
getDescription:返回當前Resource代表的底層資源的描述符,通常就是資源的全路徑(實際文件名或實際 URL地址)。

Resource接口提供了足夠的抽象,足夠滿足我們日常使用。而且提供了很多內置Resource實現: ByteArrayResource、InputStreamResource 、FileSystemResource 、UrlResource 、ClassPathResource、 ServletContextResource、VfsResource等。

2. 內置Resource實現

(1)ByteArrayResource

ByteArrayResource代表byte[]數組資源,對於“getInputStream”操作將返回一個 ByteArrayInputStream。
首先讓我們看下使用ByteArrayResource如何處理byte數組資源:

技術分享圖片
 1 package helloworld;
 2 import java.io.IOException;
 3 import java.io.InputStream;
 4 
 5 import org.junit.Test;
 6 import org.springframework.core.io.ByteArrayResource;
 7 import org.springframework.core.io.Resource;
 8 
 9 
10 public class HelloTest {
11     @Test
12     public void testByteArrayResource() {
13         Resource resource=new ByteArrayResource("Hello World!".getBytes());
14         if(resource.exists()) {
15             dumpStream(resource);
16         }
17     }
18     
19     private void dumpStream(Resource resource) {
20         InputStream is=null;
21         try {
22             //1.獲取文件資源
23             is=resource.getInputStream();
24             //2.讀取資源
25             byte[] descBytes=new byte[is.available()];
26             is.read(descBytes);
27             System.out.println(new String(descBytes));
28         }catch (IOException e) {
29             e.printStackTrace();
30         }finally {
31             try {
32                 //3.關閉資源
33                 is.close();
34             }catch (IOException e) {
35                 
36             }
37         }
38     }
39 }
40 
41 
42 Hello World!
View Code

說明:ByteArrayResource可多次讀取數組資源,即isOpen ()永遠返回false。

(2)InputStreamResource

InputStreamResource代表java.io.InputStream字節流,對於“getInputStream ”操作將直接返回該字節 流,因此只能讀取一次該字節流,即“isOpen”永遠返回true。

技術分享圖片
 1 @Test
 2     public void testInputStreamResource() {
 3         ByteArrayInputStream bis=new ByteArrayInputStream("Hello World!".getBytes());
 4         Resource resource=new InputStreamResource(bis);
 5         if(resource.exists()) {
 6             dumStream(resource);
 7         }
 8         Assert.assertEquals(true, resource.isOpen());
 9     }
10 
11 測試代碼幾乎和ByteArrayResource測試完全一樣,註意“isOpen”此處用於返回true。
View Code

(3)FileSystemResource

FileSystemResource代表java.io.File資源,對於“getInputStream ”操作將返回底層文件的字節流, “isOpen”將永遠返回false,從而表示可多次讀取底層文件的字節流。

假設存在test.txt文件,它中間的內容是haha

技術分享圖片
 1 public class HelloTest {
 2     @Test
 3     public void testByteArrayResource() {
 4         File file=new File("C:\\Users\\houuumin\\Desktop\\test.txt");
 5         Resource resource=new FileSystemResource(file);
 6         if(resource.exists()) {
 7             dumpStream(resource);
 8         }
 9         Assert.assertEquals(false, resource.isOpen());
10     }
11     
12     private void dumpStream(Resource resource) {
13         InputStream is=null;
14         try {
15             //1.獲取文件資源
16             is=resource.getInputStream();
17             //2.讀取資源
18             byte[] descBytes=new byte[is.available()];
19             is.read(descBytes);
20             System.out.println(new String(descBytes));
21         }catch (IOException e) {
22             e.printStackTrace();
23         }finally {
24             try {
25                 //3.關閉資源
26                 is.close();
27             }catch (IOException e) {
28                 
29             }
30         }
31     }
32 }
33 
34 haha
View Code

(4)ClassPathResource

ClassPathResource代表classpath路徑的資源,將使用ClassLoader進行加載資源。classpath 資源存在於類路 徑中的文件系統中或jar包裏,且“isOpen”永遠返回false,表示可多次讀取資源。
ClassPathResource加載資源替代了Class類和ClassLoader類的“getResource(String name)”和 “getResourceAsStream(String name)”兩個加載類路徑資源方法,提供一致的訪問方式。

ClassPathResource提供了三個構造器:

public ClassPathResource(String path):使用默認的ClassLoader加載“path”類路徑資源;
public ClassPathResource(String path, ClassLoader classLoader):使用指定的ClassLoader加載 “path”類路徑資源;
public ClassPathResource(String path, Class<?> clazz):使用指定的類加載“path”類路徑資源,將加 載相對於當前類的路徑的資源;
eg:最後一個舉例:

比如當前類路徑是“cn.javass.spring.chapter4.ResourceTest”,而需要加載的資源路徑是“cn/javass/spring/ chapter4/test1.properties”,則將加載的資源在“cn/javass/spring/chapter4/cn/javass/spring/chapter4/ test1.properties”;
而如果需要 加載的資源路徑為“test1.properties”,將加載的資源為“cn/javass/spring/chapter4/ test1.properties”。

1)使用默認的加載器加載資源,將加載當前ClassLoader類路徑上相對於根路徑的資源;

技術分享圖片
 1 public class HelloTest {
 2     @Test
 3     public void testClasspathResourceByDefaultClassLoader() throws IOException {
 4         Resource resource=new ClassPathResource("test1.properties");
 5         if(resource.exists()) {
 6             dumpStream(resource);
 7         }
 8         System.out.println("path:"+resource.getFile().getAbsolutePath());
 9         Assert.assertEquals(false, resource.isOpen());
10     }
11     
12     private void dumpStream(Resource resource) {
13         InputStream is=null;
14         try {
15             //1.獲取文件資源
16             is=resource.getInputStream();
17             //2.讀取資源
18             byte[] descBytes=new byte[is.available()];
19             is.read(descBytes);
20             System.out.println(new String(descBytes));
21         }catch (IOException e) {
22             e.printStackTrace();
23         }finally {
24             try {
25                 //3.關閉資源
26                 is.close();
27             }catch (IOException e) {
28                 
29             }
30         }
31     }
32 }
View Code

resource目錄下有一個文件叫做test1.properties,它中間的內容為hello world。

運行結果如下:

技術分享圖片
1 hello world
2 path:C:\Users\hermioner\eclipse-workspace\helloworld\target\test-classes\test1.properties
View Code

2)使用指定的ClassLoader進行加載資源,將加載指定的ClassLoader類路徑上相對於根路徑的資源;

技術分享圖片
 1 public class HelloTest {
 2     @Test
 3     public void testClasspathResourceByClassLoader() throws IOException {
 4         ClassLoader cl=this.getClass().getClassLoader();
 5         Resource resource=new ClassPathResource("test1.properties",cl);
 6         if(resource.exists()) {
 7             dumpStream(resource);
 8         }
 9         System.out.println("path:"+resource.getFile().getAbsolutePath());
10         Assert.assertEquals(false, resource.isOpen());
11     }
12     
13     private void dumpStream(Resource resource) {
14         InputStream is=null;
15         try {
16             //1.獲取文件資源
17             is=resource.getInputStream();
18             //2.讀取資源
19             byte[] descBytes=new byte[is.available()];
20             is.read(descBytes);
21             System.out.println(new String(descBytes));
22         }catch (IOException e) {
23             e.printStackTrace();
24         }finally {
25             try {
26                 //3.關閉資源
27                 is.close();
28             }catch (IOException e) {
29                 
30             }
31         }
32     }
33 }
View Code 技術分享圖片
1 hello world
2 path:C:\Users\hermioner\eclipse-workspace\helloworld\target\test-classes\test1.properties
View Code

note: classpath指的是編譯後的路徑地址,可以通過build path來查看:

技術分享圖片

可以看到build path的目的地是helloworld/target/test-classes中

二.訪問資源(ResourceLoader)

ResourceLoader接口用於返回Resource對象;其實現可以看作是一個生產Resource的工廠類。

1. ResourceLoader的API

技術分享圖片
1 public interface ResourceLoader { 
2      String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
3       Resource getResource(String location); 
4       ClassLoader getClassLoader(); 
5 }
View Code

getResource接口用於根據提供的location參數返回相應的Resource對象;而getClassLoader則返回加載這些 Resource的ClassLoader

Spring提供了一個適用於所有環境的DefaultResourceLoader實現,可以返回ClassPathResource、 UrlResource;還提供一個用於web環境的ServletContextResourceLoader,它繼承了DefaultResourceLoader的 所有功能,又額外提供了獲取ServletContextResource的支持。

ResourceLoader在進行加載資源時需要使用前綴來指定需要加載:“classpath:path”表示返回 ClasspathResource,“http://path”和“file:path”表示返回UrlResource資源,如果不加前綴則需要根據當前上 下文來決定,DefaultResourceLoader默認實現可以加載classpath資源,如代碼所示

技術分享圖片
 1 @Test
 2    public void testResourceLoad() {
 3        ResourceLoader loader=new DefaultResourceLoader();
 4        Resource resource=loader.getResource("classpath:com/test");
 5        Assert.assertEquals(ClassPathResource.class, resource.getClass());
 6        
 7        Resource resource2=loader.getResource("file:com/test");
 8        Assert.assertEquals(UrlResource.class, resource2.getClass());
 9        
10        Resource resource3=loader.getResource("com/test");
11        Assert.assertTrue(resource3 instanceof ClassPathResource);
12    }
View Code

2. ApplicationContext

ApplicationContext都實現了ResourceLoader,因此可以使用其來加載資源。主要有以下實現類:

ClassPathXmlApplicationContext:不指定前綴將返回默認的ClassPathResource資源,否則將根據前綴來 加載資源;

FileSystemXmlApplicationContext:不指定前綴將返回FileSystemResource,否則將根據前綴來加載資源;

WebApplicationContext:不指定前綴將返回ServletContextResource,否則將根據前綴來加載資源;

其他:不指定前綴根據當前上下文返回Resource實現,否則將根據前綴來加載資源。

3. ResourceLoaderAware接口

ResourceLoaderAware是一個標記接口,用於通過ApplicationContext上下文註入ResourceLoader。

API如下:

技術分享圖片
1 public interface ResourceLoaderAware { 
2      void setResourceLoader(ResourceLoader resourceLoader);
3  }
View Code

舉例說明Application是一個ResourceLoader:

技術分享圖片
 1 public class ResourceBean implements ResourceLoaderAware{
 2     private ResourceLoader resourceLoader;
 3     
 4     public void setResourceLoader(ResourceLoader resourceLoader) {
 5         this.resourceLoader=resourceLoader;
 6     }
 7     
 8     public ResourceLoader getResourceLoader() {
 9         return resourceLoader;
10     }
11 }
View Code 技術分享圖片
1 <bean class="com.test.spring.ResourceBean"/>
View Code 技術分享圖片
1 @Test
2     public void test() {
3         ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
4         ResourceBean resourceBean=context.getBean(ResourceBean.class);
5         ResourceLoader loader=resourceBean.getResourceLoader();
6         Assert.assertTrue(loader instanceof ApplicationContext);
7     }
View Code

註意此處“loader instanceof ApplicationContext”,說明了ApplicationContext就是個ResoureLoader.

4. 註入Resource

Spring提供了一個PropertyEditor “ResourceEditor”用於在註入的字符串和Resource之間進行轉換。因此可 以使用註入方式註入Resource。

ResourceEditor完全使用ApplicationContext根據註入的路徑字符串獲取相應的Resource.

技術分享圖片
 1 public class ResourceBean2 {
 2     private Resource resource;
 3 
 4     public Resource getResource() {
 5         return resource;
 6     }
 7 
 8     public void setResource(Resource resource) {
 9         this.resource = resource;
10     }
11     
12 
13 }
View Code 技術分享圖片
1 <bean id="bean1" class="com.test.spring.ResourceBean2">
2           <property name="resource" value="test1.properties"/>
3     </bean>
View Code 技術分享圖片
1 @Test
2     public void test() {
3         ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
4         ResourceBean2 resourceBean2=context.getBean("bean1",ResourceBean2.class);
5         Assert.assertTrue(resourceBean2.getResource() instanceof ClassPathResource);
6     }
View Code

note: 也可以支持Resource數組

note:還可以使用通配符進行匹配一批路徑

參考文獻

《spring揭秘》

https://jinnianshilongnian.iteye.com/blog/1416320

Spring08-----IoC容器ApplicationContext