Spring源码学习-容器初始化之FileSystemXmlApplicationContext(二)路径格式及解析方式(上) 推荐
2012-05-10 21:19
1156 查看
了解完了构造函数,我们回到上节《Spring源码学习-容器初始化之FileSystemXmlApplicationContext(一)构造函数》留下的思考的问题:
支持路径格式的研究。(绝对?相对?通配符?classpath格式又如何?)
配合placeholder使用的路径问题研究。
路径如何解析?
下面,我们就来一一验证和解答。
先放出本次测试用的配置文件(app-context和test.properties):
首先想到的自然是最普通的绝对路径:
测试通过,我们来看下Spring是怎么找到该文件的。之前已经说过refresh这个函数,是Spring生命周期的开始,我们就以它为入口,顺藤摸瓜,时序图如下:
最终,我们找到解析路径的关键方法,PathMatchingResourcePatternResolver的getResources方法和DefaultResourceLoader中的getResource方法:
其中常量
CLASSPATH_ALL_URL_PREFIX = "classpath*:";
CLASSPATH_URL_PREFIX = "classpath:";
我们输入的路径是绝对路径:"D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\app-context.xml"。不是以classpath*开头的,所以会落入else之中。在else中:getPathMatcher().isPattern(),实际是调用AntPathMatcher中的isPattern()方法:
是用来判断":"以后的路径中是否包含通配符“*”或者 "?"。
我们的路径显然也不包含,所以最终会直接走入getResource方法。
仍然,路径既不是以classpath开头的,也不是URL格式的路径,所以最终会落入 getResourceByPath(location)这个分支,而我们之前介绍过,这个方法恰好是在FileSystemXmlApplicationContext这个类中复写过的:
我们给的路径不是以"/"开头,所以直接构造了一个FileSystemResource:
即用路径直接构造了一个File。这里StringUtil.cleanPath方法:
主要是将传入的路径规范化,比如将windows的路径分隔符“\\”替换为标准的“/“,如果路径中含有.(当前文件夹),或者..(上层文件夹),则计算出其真实路径。而File本身是支持这样的路径的,也就是说,spring可以支持这样的路径。出于好奇,我们也针对这个方法测试如下:
容器可以正常初始化。路径计算正确。
补充说明:Spring最终读取配置文件,是通过InputStream加载的,Spring中的各种Resource的最上层接口InputStreamResource中定义了唯一的一个方法getInputStream。也就是说,只要保证各Resource的实现类的getInputStream方法能够正常获取流,Spring容器即可解析初始化。对于FileSystemResource而已,其实现如下:
所以,我们说,此时只有是File正常支持的格式,Spring才能正常初始化。
继续回到前面的话题。我们目前只验证else分支中的catch分支。根据代码分析,即使是FileSystemXmlApplicationContext也可以支持Classpath格式的路径和URL格式的路径的。验证如下:
验证通过,并且通过debug确认,确实走入了相应的分支,分别构造了UrlResource和ClassPathResource实例。所以,之后Spring会分别调用这个两个Resource中的getInputStream方法获取流,解析配置文件。附上这两个类中的getInputStream方法,有兴趣的可以继续研究:
上述两个实现所属的类,我想应该一目了然吧~~
至此,我们算是分析验证通过了一个小分支下的支持的路径的情况,其实,这只是这些都是最简单直接的。回想刚才的分析,如果路径包含通配符(?,*)spring是怎么处理的?如果是以classpath*开头的又是如何呢??鉴于害怕文章过长,我们下回分解…………o(∩_∩)o
支持路径格式的研究。(绝对?相对?通配符?classpath格式又如何?)
配合placeholder使用的路径问题研究。
路径如何解析?
下面,我们就来一一验证和解答。
先放出本次测试用的配置文件(app-context和test.properties):
<bean id="placeHolderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" /> <property name="locations"> <list> <value>classpath*:spring/test.properties</value> </list> </property> </bean> <bean id="veryCommonBean" class="kubi.coder.bean.VeryCommonBean"> <property name="name" value="${test.name}"></property> </bean>
test.name=verycommonbean-name
首先想到的自然是最普通的绝对路径:
/** * 测试通过普通的绝对路径: * <p>D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\app-context.xml</p> * 读取配置文件 * * @author lihzh * @date 2012-5-5 上午10:53:53 */ @Test public void testPlainAbsolutePath() { String path = "D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\app-context.xml"; ApplicationContext appContext = new FileSystemXmlApplicationContext(path); assertNotNull(appContext); VeryCommonBean bean = appContext.getBean(VeryCommonBean.class); assertNotNull(bean); assertEquals("verycommonbean-name", bean.getName()); }
测试通过,我们来看下Spring是怎么找到该文件的。之前已经说过refresh这个函数,是Spring生命周期的开始,我们就以它为入口,顺藤摸瓜,时序图如下:
最终,我们找到解析路径的关键方法,PathMatchingResourcePatternResolver的getResources方法和DefaultResourceLoader中的getResource方法:
public Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // a class path resource (multiple resources for same name possible) if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // a class path resource pattern return findPathMatchingResources(locationPattern); } else { // all class path resources with the given name return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { // Only look for a pattern after a prefix here // (to not get fooled by a pattern symbol in a strange prefix). int prefixEnd = locationPattern.indexOf(":") + 1; if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // a file pattern return findPathMatchingResources(locationPattern); } else { // a single resource with the given name return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } }
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // Try to parse the location as a URL... URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. return getResourceByPath(location); } } }
其中常量
CLASSPATH_ALL_URL_PREFIX = "classpath*:";
CLASSPATH_URL_PREFIX = "classpath:";
我们输入的路径是绝对路径:"D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\app-context.xml"。不是以classpath*开头的,所以会落入else之中。在else中:getPathMatcher().isPattern(),实际是调用AntPathMatcher中的isPattern()方法:
public boolean isPattern(String path) { return (path.indexOf('*') != -1 || path.indexOf('?') != -1); }
是用来判断":"以后的路径中是否包含通配符“*”或者 "?"。
我们的路径显然也不包含,所以最终会直接走入getResource方法。
仍然,路径既不是以classpath开头的,也不是URL格式的路径,所以最终会落入 getResourceByPath(location)这个分支,而我们之前介绍过,这个方法恰好是在FileSystemXmlApplicationContext这个类中复写过的:
protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }
我们给的路径不是以"/"开头,所以直接构造了一个FileSystemResource:
public FileSystemResource(String path) { Assert.notNull(path, "Path must not be null"); this.file = new File(path); this.path = StringUtils.cleanPath(path); }
即用路径直接构造了一个File。这里StringUtil.cleanPath方法:
主要是将传入的路径规范化,比如将windows的路径分隔符“\\”替换为标准的“/“,如果路径中含有.(当前文件夹),或者..(上层文件夹),则计算出其真实路径。而File本身是支持这样的路径的,也就是说,spring可以支持这样的路径。出于好奇,我们也针对这个方法测试如下:
/** * 测试通过含有.或者..的绝对路径 * <p>D:\\workspace-home\\spring-custom\\.\\src\\main\\resources\\spring\\..\\spring\\app-context.xml</p> * 读取配置文件 * * @author lihzh * @date 2012-5-5 上午10:53:53 */ @Test public void testContainDotAbsolutePath() { String path = "D:\\workspace-home\\spring-custom\\.\\src\\main\\resources\\spring\\..\\spring\\app-context.xml"; ApplicationContext appContext = new FileSystemXmlApplicationContext(path); assertNotNull(appContext); VeryCommonBean bean = appContext.getBean(VeryCommonBean.class); assertNotNull(bean); assertEquals("verycommonbean-name", bean.getName()); }
容器可以正常初始化。路径计算正确。
补充说明:Spring最终读取配置文件,是通过InputStream加载的,Spring中的各种Resource的最上层接口InputStreamResource中定义了唯一的一个方法getInputStream。也就是说,只要保证各Resource的实现类的getInputStream方法能够正常获取流,Spring容器即可解析初始化。对于FileSystemResource而已,其实现如下:
/** * This implementation opens a FileInputStream for the underlying file. * @see java.io.FileInputStream */ public InputStream getInputStream() throws IOException { return new FileInputStream(this.file); }
所以,我们说,此时只有是File正常支持的格式,Spring才能正常初始化。
继续回到前面的话题。我们目前只验证else分支中的catch分支。根据代码分析,即使是FileSystemXmlApplicationContext也可以支持Classpath格式的路径和URL格式的路径的。验证如下:
/** * 测试通过含有.或者..的绝对路径 * <p>file:/D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\app-context.xml</p> * 读取配置文件 * * @author lihzh * @date 2012-5-5 上午10:53:53 */ @Test public void testURLAbsolutePath() { String path = "file:/D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\app-context.xml"; ApplicationContext appContext = new FileSystemXmlApplicationContext(path); assertNotNull(appContext); VeryCommonBean bean = appContext.getBean(VeryCommonBean.class); assertNotNull(bean); assertEquals("verycommonbean-name", bean.getName()); } /** * 测试通过Classpath类型的路径 * <p>classpath:spring/app-context.xml</p> * 通过读取配置文件 * * @author lihzh * @date 2012-5-5 上午10:53:53 */ @Test public void testClassPathStylePath() { String path = "classpath:spring/app-context.xml"; ApplicationContext appContext = new FileSystemXmlApplicationContext(path); assertNotNull(appContext); VeryCommonBean bean = appContext.getBean(VeryCommonBean.class); assertNotNull(bean); assertEquals("verycommonbean-name", bean.getName()); }
验证通过,并且通过debug确认,确实走入了相应的分支,分别构造了UrlResource和ClassPathResource实例。所以,之后Spring会分别调用这个两个Resource中的getInputStream方法获取流,解析配置文件。附上这两个类中的getInputStream方法,有兴趣的可以继续研究:
/** * This implementation opens an InputStream for the given URL. * It sets the "UseCaches" flag to <code>false</code>, * mainly to avoid jar file locking on Windows. * @see java.net.URL#openConnection() * @see java.net.URLConnection#setUseCaches(boolean) * @see java.net.URLConnection#getInputStream() */ public InputStream getInputStream() throws IOException { URLConnection con = this.url.openConnection(); ResourceUtils.useCachesIfNecessary(con); try { return con.getInputStream(); } catch (IOException ex) { // Close the HTTP connection (if applicable). if (con instanceof HttpURLConnection) { ((HttpURLConnection) con).disconnect(); } throw ex; } } /** * This implementation opens an InputStream for the given class path resource. * @see java.lang.ClassLoader#getResourceAsStream(String) * @see java.lang.Class#getResourceAsStream(String) */ public InputStream getInputStream() throws IOException { InputStream is; if (this.clazz != null) { is = this.clazz.getResourceAsStream(this.path); } else { is = this.classLoader.getResourceAsStream(this.path); } if (is == null) { throw new FileNotFoundException( getDescription() + " cannot be opened because it does not exist"); } return is; }
上述两个实现所属的类,我想应该一目了然吧~~
至此,我们算是分析验证通过了一个小分支下的支持的路径的情况,其实,这只是这些都是最简单直接的。回想刚才的分析,如果路径包含通配符(?,*)spring是怎么处理的?如果是以classpath*开头的又是如何呢??鉴于害怕文章过长,我们下回分解…………o(∩_∩)o
相关文章推荐
- Spring源码学习-容器初始化之FileSystemXmlApplicationContext(一)构造函数 推荐
- Spring源码学习-容器初始化之FileSystemXmlApplicationContext(一)构造函数
- JunitTest拿spring容器bean的2种方式[Spring ClassPathXmlApplicationContext和FileSystemXmlApplicationContext]
- Spring中ClassPathXmlApplication与FileSystemXmlApplicationContext的区别以及ClassPathXmlApplicationContext 的具体路径
- Spring源码学习--Spring配置解析文件ApplicationContext.xml(一)
- spring源码学习笔记-初始化(六) ClassPathXmlApplicationContext
- Spring中关于ClassPathXmlApplicationContext和FileSystemXmlApplicationContext的路径设置
- Spring ApplicationContext 容器 FileSystemXmlApplicationContext
- spring中ClassPathXmlApplicationContext和FileSystemXmlApplicationContext的路径问题
- Spring源码学习之一 ContextLoaderListener及XmlWebApplicationContext
- Spring IOC 源码-ClassPathXmlApplicationContext-bean解析
- ClassPathXmlApplicationContext和FileSystemXmlApplicationContext的路径设置祥解
- spring中ClassPathXmlApplicationContext和FileSystemXmlApplicationContext的区别
- Spring--FileSystemXmlApplicationContext
- FileSystemApplicationContext创建spring上下文,路径都只被识别为相对路径
- Spring ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
- 小读spring ioc源码(三)——XmlWebApplicationContext初始化的整体过程
- 【spring 区别】ClassXmlAplicationContext和FileSystemXmlApplicationContext的区别
- Spring--FileSystemXmlApplicationContext