Spring08-----IoC容器ApplicationContext

世事洞明皆学问,人情练达即文章。这篇文章主要讲述Spring08-----IoC容器ApplicationContext相关的知识,希望能为你提供帮助。
        作为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数组资源:
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
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 12public void testByteArrayResource() { 13Resource resource=new ByteArrayResource("Hello World!".getBytes()); 14if(resource.exists()) { 15dumpStream(resource); 16} 17} 18 19private void dumpStream(Resource resource) { 20InputStream is=null; 21try { 22//1.获取文件资源 23is=resource.getInputStream(); 24//2.读取资源 25byte[] descBytes=new byte[is.available()]; 26is.read(descBytes); 27System.out.println(new String(descBytes)); 28}catch (IOException e) { 29e.printStackTrace(); 30}finally { 31try { 32//3.关闭资源 33is.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。
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 @Test 2public void testInputStreamResource() { 3ByteArrayInputStream bis=new ByteArrayInputStream("Hello World!".getBytes()); 4Resource resource=new InputStreamResource(bis); 5if(resource.exists()) { 6dumStream(resource); 7} 8Assert.assertEquals(true, resource.isOpen()); 9} 10 11 测试代码几乎和ByteArrayResource测试完全一样,注意“isOpen”此处用于返回true。

View Code(3)FileSystemResource
FileSystemResource代表java.io.File资源,对于“getInputStream ”操作将返回底层文件的字节流, “isOpen”将永远返回false,从而表示可多次读取底层文件的字节流。
假设存在test.txt文件,它中间的内容是haha
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 public class HelloTest { 2@Test 3public void testByteArrayResource() { 4File file=new File("C:\\\\Users\\\\houuumin\\\\Desktop\\\\test.txt"); 5Resource resource=new FileSystemResource(file); 6if(resource.exists()) { 7dumpStream(resource); 8} 9Assert.assertEquals(false, resource.isOpen()); 10} 11 12private void dumpStream(Resource resource) { 13InputStream is=null; 14try { 15//1.获取文件资源 16is=resource.getInputStream(); 17//2.读取资源 18byte[] descBytes=new byte[is.available()]; 19is.read(descBytes); 20System.out.println(new String(descBytes)); 21}catch (IOException e) { 22e.printStackTrace(); 23}finally { 24try { 25//3.关闭资源 26is.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类路径上相对于根路径的资源;
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

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

View Coderesource目录下有一个文件叫做test1.properties,它中间的内容为hello world。
运行结果如下:
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 hello world 2 path:C:\\Users\\hermioner\\eclipse-workspace\\helloworld\\target\\test-classes\\test1.properties

View Code2)使用指定的ClassLoader进行加载资源,将加载指定的ClassLoader类路径上相对于根路径的资源;
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 public class HelloTest { 2@Test 3public void testClasspathResourceByClassLoader() throws IOException { 4ClassLoader cl=this.getClass().getClassLoader(); 5Resource resource=new ClassPathResource("test1.properties",cl); 6if(resource.exists()) { 7dumpStream(resource); 8} 9System.out.println("path:"+resource.getFile().getAbsolutePath()); 10Assert.assertEquals(false, resource.isOpen()); 11} 12 13private void dumpStream(Resource resource) { 14InputStream is=null; 15try { 16//1.获取文件资源 17is=resource.getInputStream(); 18//2.读取资源 19byte[] descBytes=new byte[is.available()]; 20is.read(descBytes); 21System.out.println(new String(descBytes)); 22}catch (IOException e) { 23e.printStackTrace(); 24}finally { 25try { 26//3.关闭资源 27is.close(); 28}catch (IOException e) { 29 30} 31} 32} 33 }

View Code
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 hello world 2 path:C:\\Users\\hermioner\\eclipse-workspace\\helloworld\\target\\test-classes\\test1.properties

View Codenote: classpath指的是编译后的路径地址,可以通过build path来查看:
Spring08-----IoC容器ApplicationContext

文章图片

可以看到build path的目的地是helloworld/target/test-classes中
二.访问资源(ResourceLoader)ResourceLoader接口用于返回Resource对象;其实现可以看作是一个生产Resource的工厂类。
1. ResourceLoader的API
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 public interface ResourceLoader { 2String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; 3Resource getResource(String location); 4ClassLoader getClassLoader(); 5 }

View CodegetResource接口用于根据提供的location参数返回相应的Resource对象;而getClassLoader则返回加载这些 Resource的ClassLoader
        Spring提供了一个适用于所有环境的DefaultResourceLoader实现,可以返回ClassPathResource、 UrlResource;还提供一个用于web环境的ServletContextResourceLoader,它继承了DefaultResourceLoader的 所有功能,又额外提供了获取ServletContextResource的支持。
        ResourceLoader在进行加载资源时需要使用前缀来指定需要加载:“classpath:path”表示返回 ClasspathResource,“http://path”和“file:path”表示返回UrlResource资源,如果不加前缀则需要根据当前上 下文来决定,DefaultResourceLoader默认实现可以加载classpath资源,如代码所示 
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

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

View Code2. ApplicationContext
ApplicationContext都实现了ResourceLoader,因此可以使用其来加载资源。主要有以下实现类:
ClassPathXmlApplicationContext:不指定前缀将返回默认的ClassPathResource资源,否则将根据前缀来 加载资源;
FileSystemXmlApplicationContext:不指定前缀将返回FileSystemResource,否则将根据前缀来加载资源;
WebApplicationContext:不指定前缀将返回ServletContextResource,否则将根据前缀来加载资源;
其他:不指定前缀根据当前上下文返回Resource实现,否则将根据前缀来加载资源。
3. ResourceLoaderAware接口
ResourceLoaderAware是一个标记接口,用于通过ApplicationContext上下文注入ResourceLoader。
API如下:
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 public interface ResourceLoaderAware { 2void setResourceLoader(ResourceLoader resourceLoader); 3}

View Code举例说明Application是一个ResourceLoader:
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 public class ResourceBean implements ResourceLoaderAware{ 2private ResourceLoader resourceLoader; 3 4public void setResourceLoader(ResourceLoader resourceLoader) { 5this.resourceLoader=resourceLoader; 6} 7 8public ResourceLoader getResourceLoader() { 9return resourceLoader; 10} 11 }

View Code
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 < bean class="com.test.spring.ResourceBean"/>

View Code
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 @Test 2public void test() { 3ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); 4ResourceBean resourceBean=context.getBean(ResourceBean.class); 5ResourceLoader loader=resourceBean.getResourceLoader(); 6Assert.assertTrue(loader instanceof ApplicationContext); 7}

View Code注意此处“loader instanceof ApplicationContext”,说明了ApplicationContext就是个ResoureLoader.
4. 注入Resource
Spring提供了一个PropertyEditor “ResourceEditor”用于在注入的字符串和Resource之间进行转换。因此可 以使用注入方式注入Resource。
ResourceEditor完全使用ApplicationContext根据注入的路径字符串获取相应的Resource.
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 public class ResourceBean2 { 2private Resource resource; 3 4public Resource getResource() { 5return resource; 6} 7 8public void setResource(Resource resource) { 9this.resource = resource; 10} 11 12 13 }

View Code
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 < bean id="bean1" class="com.test.spring.ResourceBean2"> 2< property name="resource" value="https://www.songbingjia.com/android/test1.properties"/> 3< /bean>

View Code
Spring08-----IoC容器ApplicationContext

文章图片
Spring08-----IoC容器ApplicationContext

文章图片
1 @Test 2public void test() { 3ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); 4ResourceBean2 resourceBean2=context.getBean("bean1",ResourceBean2.class); 5Assert.assertTrue(resourceBean2.getResource() instanceof ClassPathResource); 6}

View Codenote: 也可以支持Resource数组
note:还可以使用通配符进行匹配一批路径
 
参考文献
《spring揭秘》
【Spring08-----IoC容器ApplicationContext】https://jinnianshilongnian.iteye.com/blog/1416320

    推荐阅读