4.3.1 ResourceLoader接口
ResourceLoader接口用於返回Resource對象;其實現可以看作是一個生產Resource的工廠類。
public interface ResourceLoader { Resource getResource(String location); ClassLoader getClassLoader(); }
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資源,如代碼所示(cn.javass.spring.chapter4.ResourceLoaderTest):
@Test public void testResourceLoad() { ResourceLoader loader = new DefaultResourceLoader(); Resource resource = loader.getResource("classpath:cn/javass/spring/chapter4/test1.txt"); //驗證返回的是ClassPathResource Assert.assertEquals(ClassPathResource.class, resource.getClass()); Resource resource2 = loader.getResource("file:cn/javass/spring/chapter4/test1.txt"); //驗證返回的是ClassPathResource Assert.assertEquals(UrlResource.class, resource2.getClass()); Resource resource3 = loader.getResource("cn/javass/spring/chapter4/test1.txt"); //驗證返默認可以加載ClasspathResource Assert.assertTrue(resource3 instanceof ClassPathResource); }
對於目前所有ApplicationContext都實現了ResourceLoader,因此可以使用其來加載資源。
ClassPathXmlApplicationContext:不指定前綴將返回默認的ClassPathResource資源,否則將根據前綴來加載資源;
FileSystemXmlApplicationContext:不指定前綴將返回FileSystemResource,否則將根據前綴來加載資源;
WebApplicationContext:不指定前綴將返回ServletContextResource,否則將根據前綴來加載資源;
其他:不指定前綴根據當前上下文返回Resource實現,否則將根據前綴來加載資源。
4.3.2 ResourceLoaderAware接口
ResourceLoaderAware是一個標記接口,用於通過ApplicationContext上下文注入ResourceLoader。
public interface ResourceLoaderAware { void setResourceLoader(ResourceLoader resourceLoader); }
讓我們看下測試代碼吧:
1) 首先准備測試Bean,我們的測試Bean還簡單只需實現ResourceLoaderAware接口,然后通過回調將ResourceLoader保存下來就可以了:
package cn.javass.spring.chapter4.bean; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.ResourceLoader; public class ResourceBean implements ResourceLoaderAware { private ResourceLoader resourceLoader; @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } public ResourceLoader getResourceLoader() { return resourceLoader; } }
2) 配置Bean定義(chapter4/resourceLoaderAware.xml):
<bean class="cn.javass.spring.chapter4.bean.ResourceBean"/>
3)測試(cn.javass.spring.chapter4.ResoureLoaderAwareTest):
@Test public void test() { ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter4/resourceLoaderAware.xml"); ResourceBean resourceBean = ctx.getBean(ResourceBean.class); ResourceLoader loader = resourceBean.getResourceLoader(); Assert.assertTrue(loader instanceof ApplicationContext); }
注意此處“loader instanceof ApplicationContext”,說明了ApplicationContext就是個ResoureLoader。
由於上述實現回調接口注入ResourceLoader的方式屬於侵入式,所以不推薦上述方法,可以采用更好的自動注入方式,如“byType”和“constructor”,此處就不演示了。
4.3.3 注入Resource
通過回調或注入方式注入“ResourceLoader”,然后再通過“ResourceLoader”再來加載需要的資源對於只需要加載某個固定的資源是不是很麻煩,有沒有更好的方法類似於前邊實例中注入“java.io.File”類似方式呢?
Spring提供了一個PropertyEditor “ResourceEditor”用於在注入的字符串和Resource之間進行轉換。因此可以使用注入方式注入Resource。
ResourceEditor完全使用ApplicationContext根據注入的路徑字符串獲取相應的Resource,說白了還是自己做還是容器幫你做的問題。
接下讓我們看下示例:
1)准備Bean:
package cn.javass.spring.chapter4.bean; import org.springframework.core.io.Resource; public class ResourceBean3 { private Resource resource; public Resource getResource() { return resource; } public void setResource(Resource resource) { this.resource = resource; } }
2)准備配置文件(chapter4/ resourceInject.xml):
<bean id="resourceBean1" class="cn.javass.spring.chapter4.bean.ResourceBean3"> <property name="resource" value="cn/javass/spring/chapter4/test1.properties"/> </bean> <bean id="resourceBean2" class="cn.javass.spring.chapter4.bean.ResourceBean3"> <property name="resource" value="classpath:cn/javass/spring/chapter4/test1.properties"/> </bean>
注意此處“resourceBean1”注入的路徑沒有前綴表示根據使用的ApplicationContext實現進行選擇Resource實現。
3)讓我們來看下測試代碼(cn.javass.spring.chapter4.ResourceInjectTest)吧:
@Test public void test() { ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter4/resourceInject.xml"); ResourceBean3 resourceBean1 = ctx.getBean("resourceBean1", ResourceBean3.class); ResourceBean3 resourceBean2 = ctx.getBean("resourceBean2", ResourceBean3.class); Assert.assertTrue(resourceBean1.getResource() instanceof ClassPathResource); Assert.assertTrue(resourceBean2.getResource() instanceof ClassPathResource); }
接下來一節讓我們深入ApplicationContext對各種Resource的支持,及如何使用更便利的資源加載方式。