8.3.4 在ApplicationContext中使用资源
不管以怎样的方式创建ApplicationContext实例,都需要为ApplicationContext指定配置文件,Spring允许使用一份或多分XML配置文件。
当程序创建ApplicationContext实例时,通常也是以Resource的方式来访问配置文件的,所以ApplicationContext完全支持ClassPathResource、FileSystemResource、ServletContextResource等资源访问方式。
ApplicationContext确定资源访问策略通常有两种方法:
⊙ 使用ApplicationContext实现类指定访问策略。
⊙ 使用前缀指定访问策略。
1.使用ApplicationContext实现类指定访问策略
创建ApplicationContext对象时,通常可以使用如下三个实现类:
⊙ ClassPathXMLApplicationContext : 对应使用ClassPathResource进行资源访问。
⊙ FileSystemXmlApplicationContext : 对应使用FileSystemResource进行资源访问。
⊙ XmlWebApplicationContext : 对应使用ServletContextResource进行资源访问。
当使用ApplicationContext的不同实现类时,就意味着Spring使用响应的资源访问策略。
2.使用前缀指定访问策略
Spring允许使用前缀来指定资源访问策略。
package edu.pri.lime._8_3_4.prefix.bean.main; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.io.Resource; public class SpringTest { public static void main(String[] args) { /* * 通过搜索文件系统路径下的xml文件创建ApplicationContext, * 但通过指定classpath:前缀强制搜索类加载路径*/ ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:app_8_3_4.xml"); System.out.println(ctx); /*使用ApplicationContext的资源访问策略来访问资源,没有指定前缀*/ Resource resource = ctx.getResource("book.xml"); System.out.println(resource.getFilename()); System.out.println(resource.getDescription()); } }
Console :
org.springframework.context.support.FileSystemXmlApplicationContext@2752f6e2: startup date [Sun Feb 12 19:50:38 CST 2017]; root of context hierarchy
book.xml
file [E:UsersAdministratorworkspacelimeook.xml]
系统从类加载路径下搜索xml;但使用ApplicationContext来访问资源时,依然采用的是FileSystemResource实现类,这与FileSystemXmlApplicationContext的访问策略是一致的。这表明:通过classpath:前缀指定资源访问策略仅仅对档次访问有效,程序后面进行资源访问时,还会根据ApplicationContext的实现类来选择对应的资源访问策略。
3.classpath*:前缀的用法
classpath*:前缀提供了加载多个XML配置文件的能力,当使用classpath*:前缀来指定XML配置文件时,系统将搜索类加载路径,找到所有与文件名匹配的文件,分别加载文件中的配置定义,最后合并成一个ApplicationContext。
package edu.pri.lime._8_3_4.prefixStart.bean.main; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class SpringTest { public static void main(String[] args) { ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath*:app_8_3_4.xml"); System.out.println(ctx); } }
将配置文件app_8_3_4.xml分别放在应用的classes路径(该路径被设为类加载路径之一)下,并将配置文件放在classes/8.3.4.3路径下(该路径也被设为类加载路径之一),程序实例化ApplicationContext时显示:
为什么不是想象中的两个Loading XML bean definitions? 信息: Loading XML bean definitions from URL [file:/E:/Users/Administrator/workspace/lime/target/classes/app_8_3_4.xml] org.springframework.context.support.FileSystemXmlApplicationContext@2752f6e2: startup date [Mon Feb 13 21:30:37 CST 2017]; root of context hierarchy
当使用classpath*:前缀时,Spring将会搜索类加载路径下所有满足该规则的配置文件。
如果不是采用classpath*:前缀,而是改为使用classpath:前缀,Spring则只加载第一个符合条件的XML文件。
当使用classpath:前缀时,系统通过类加载路径搜索xml文件,如果找到文件名匹配的文件,系统立即停止搜索,加载该文件,即使有多个文件名匹配的文件,系统也只加载第一个文件。资源文件的搜索顺序取决于类加载路径的顺序,排在前面的配置文件将优先被加载。
注意 :
classpath*: 前缀仅对ApplicationContext有效。实际情况是,创建ApplicationContext时,分别访问多个配置文件(通过ClassLoader的getResource方法实现)。因此,classpath*:前缀不可用于Resource,使用classpath*:前缀一次性访问多个资源是行不通的。
一次性加载多个配置文件的方式:指定配置文件时使用通配符。
ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:app_8_3*.xml");
Console :
二月 13, 2017 9:56:35 下午 org.springframework.context.support.FileSystemXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@2752f6e2: startup date [Mon Feb 13 21:56:35 CST 2017]; root of context hierarchy 二月 13, 2017 9:56:35 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from file [E:UsersAdministratorworkspacelime argetclassesapp_8_3_2.xml] 二月 13, 2017 9:56:35 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from file [E:UsersAdministratorworkspacelime argetclassesapp_8_3_3.xml] 二月 13, 2017 9:56:35 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory registerBeanDefinition 信息: Overriding bean definition for bean 'testBean': replacing [Generic bean: class [edu.pri.lime._8_3_2.bean.impl.TestBean]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [E:UsersAdministratorworkspacelime argetclasseseduprilime\_8_3_2eanimplTestBean.class]] with [Generic bean: class [edu.pri.lime._8_3_3.bean.impl.TestBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [E:UsersAdministratorworkspacelime argetclassesapp_8_3_3.xml]] 二月 13, 2017 9:56:35 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from file [E:UsersAdministratorworkspacelime argetclassesapp_8_3_4.xml]
Spring允许将classpath*:前缀和通配符结合使用:
ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath*:app_8_3*.xml");
Console :
然而并没有什么不同。。。 二月 13, 2017 9:57:58 下午 org.springframework.context.support.FileSystemXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@2752f6e2: startup date [Mon Feb 13 21:57:58 CST 2017]; root of context hierarchy 二月 13, 2017 9:57:58 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from file [E:UsersAdministratorworkspacelime argetclassesapp_8_3_2.xml] 二月 13, 2017 9:57:58 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from file [E:UsersAdministratorworkspacelime argetclassesapp_8_3_3.xml] 二月 13, 2017 9:57:58 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory registerBeanDefinition 信息: Overriding bean definition for bean 'testBean': replacing [Generic bean: class [edu.pri.lime._8_3_2.bean.impl.TestBean]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [E:UsersAdministratorworkspacelime argetclasseseduprilime\_8_3_2eanimplTestBean.class]] with [Generic bean: class [edu.pri.lime._8_3_3.bean.impl.TestBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [E:UsersAdministratorworkspacelime argetclassesapp_8_3_3.xml]] 二月 13, 2017 9:57:58 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from file [E:UsersAdministratorworkspacelime argetclassesapp_8_3_4.xml]
4.file:前缀的用法(然而并没有实现。。。why?)
package edu.pri.lime._8_3_4.prefixFile.bean.main; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.io.Resource; public class SpringTest { public static void main(String[] args) { ApplicationContext ctx1 = new FileSystemXmlApplicationContext("app_8_3_4_4.xml"); System.out.println(ctx1); ApplicationContext ctx2 = new FileSystemXmlApplicationContext("/app_8_3_4_4.xml"); System.out.println(ctx2); Resource resource = ctx2.getResource("app_8_3_4.xml"); System.out.println(resource.getDescription()); } }
当FileSystemXmlApplicationContext作为ResourceLoader使用时,FileSysteTemApplicationContext会简单地让所有绑定的FileSystemResource实例把绝对路径都当成相对路径处理,而不管是否以斜杠开头。
如果程序中需要访问绝对路径,则不要直接使用FileSystemResource或FileSystemXmlApplicationContext来指定绝对路径。建议强制使用file:前缀来区分相对路径和绝对路径。
ApplicationContext ctx1 = new FileSystemXmlApplicationContext("file:app_8_3_4_4.xml"); ApplicationContext ctx2 = new FileSystemXmlApplicationContext("file:/app_8_3_4_4.xml");
啦啦啦
啦啦啦