zoukankan      html  css  js  c++  java
  • 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数组资源:

     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:Usershermionereclipse-workspacehelloworld	arget	est-classes	est1.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:Usershermionereclipse-workspacehelloworld	arget	est-classes	est1.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

  • 相关阅读:
    [Castle]Asp.Net中获取Castle容器中的服务的另一方法
    IBatis.Net如何支持多个数据库
    [Castle]Castle.Model被Castle.Core代替了
    [Castle]Castle也范型
    Since NHibernate 1.2.0, objects are lazy by default
    [django]newforms两种方式示例
    [django]the story about Django and TurboGears
    在docker中运行ElasticSearch时报错:docker: invalid reference format: repository name must be lowercase.
    连接Kibana报错:Kibana server is not ready yet
    Win10系统开启虚拟机屏幕蓝屏自动重启
  • 原文地址:https://www.cnblogs.com/Hermioner/p/10194500.html
Copyright © 2011-2022 走看看