使用InputStreamResource构造XmlBeanFactory出错原因

问题:
读取spring配置文件时, 如果使用InputStreamResource构造XmlBeanFactory时出错。

InputStream inputStream = null;
		try {
			File file = new File("D:/java/.../applicationContext.xml");
			inputStream = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			System.out.println("File Not Found!");
			e.printStackTrace();
		}
		Resource resource = new InputStreamResource(inputStream);
		BeanFactory beanFactory = new XmlBeanFactory(resource);
错误信息:
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: Passed-in Resource [resource loaded through InputStream] contains an open stream: cannot determine validation mode automatically. Either pass in a Resource that is able to create fresh streams, or explicitly specify the validationMode on your XmlBeanDefinitionReader instance.at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.detectValidationMode(XmlBeanDefinitionReader.java:418)运行环境:jdk1.6 ,  eclipse3.4 ,  spring 2.o

原因分析: 

    看spring2.0源码中的 InputStreamResource类中的 isOpen() 方法:

/**
	 * This implementation always returns <code>true</code>.
	 */
	public boolean isOpen() {
		return true;
	}

这个方法永远返回true。

    再看这个方法:

/**
	 * Detects which kind of validation to perform on the XML file identified
	 * by the supplied {@link Resource}. If the
	 * file has a <code>DOCTYPE</code> definition then DTD validation is used
	 * otherwise XSD validation is assumed.
	 */
	protected int detectValidationMode(Resource resource) {
		if (resource.isOpen()) {
			throw new BeanDefinitionStoreException(
					"Passed-in Resource [" + resource + "] contains an open stream: " +
					"cannot determine validation mode automatically. Either pass in a Resource " +
					"that is able to create fresh streams, or explicitly specify the validationMode " +
					"on your XmlBeanDefinitionReader instance.");
		}
          ... ...
    }

    由于resource.isOpen()永远返回true,所以这里会抛出异常。我的理解是:InputStreamResource是直指向输入流InputStream的,在构造InputStreamResource时,我们要加载的资源第一次被打开后, 即InputStreamResource的getInputStream()方法调用后,  不允许再次读取资源对应的InputStream。下面是getInputStream()方法,可以看到,InputStreamResource用私有的属性 read来标记输入流是否被读取过, 若读取过一次, 再次读取会抛异常的。

/**
	 * This implementation throws IllegalStateException if attempting to
	 * read the underlying stream multiple times.
	 */
	public InputStream getInputStream() throws IOException, IllegalStateException {
		if (this.read) {
			throw new IllegalStateException("InputStream has already been read - " +
					"do not use InputStreamResource if a stream needs to be read multiple times");
		}
		this.read = true;
		return this.inputStream;
	}

    然而从spring2.0开始,为了支持xml schema,需要再次读取资源以检查配置文件是否遵循了DTD 和 xml schema, 又由于当前被打开的资源不能被多次读取, 以致异常抛出。从下面spring源码中关于InputStreamResource类的说明中可以看到,作者强烈建议不要使用InputStreamResource, 而是尽量使用替代者ByteArrayResource、ClassPathResource、FileSystemResource、UrlResource等来加载xml等资源
     InputStreamResource类是为给定的InputStream而准备的Resource接口的实现。它只有在没有其它合适的Resource接口实现类可用时才使用。而且,只要有可能就尽量使用ByteArrayResource或者其它基于文件的Resource实现。与其它Resource实现不同的是,这是个已经打开资源的描述符(因此isOpen()函数返回 true)。如果你需要在其它位置保持这个资源的描述符或者多次读取一个流,请不要使用它。

/**
   23    * {@link Resource} implementation for a given InputStream. Should only
   24    * be used if no specific Resource implementation is applicable.
   25    * In particular, prefer {@link ByteArrayResource} or any of the
   26    * file-based Resource implementations where possible.
   27    *
   28    * <p>In contrast to other Resource implementations, this is a descriptor
   29    * for an <i>already opened</i> resource - therefore returning "true" from
   30    * <code>isOpen()</code>. Do not use it if you need to keep the resource
   31    * descriptor somewhere, or if you need to read a stream multiple times.
   32    *
   33    * @author Juergen Hoeller
   34    * @since 28.12.2003
   35    * @see ByteArrayResource
   36    * @see ClassPathResource
   37    * @see FileSystemResource
   38    * @see UrlResource
   39    */
   40   public class InputStreamResource extends AbstractResource {...}


引一段springsource站网友的解释, 供参考: 
     This is caused by Spring 2.0's new support for XML schema: We need to sneak into the file to find out whether it's based on our DTD or our schema now. Simply assuming the DTD as default in that case could cause strange exceptions in the XML parser that would only arise with such an InputStream specified.

     The recommended solution is the same as the general resource loading recommendation of the past two years:Do not use InputStreamResource unless explicitly necessary.

总结:

    1.  我觉得要清晰理解该问题的原因,需要先了解spring2.0资源resource接口和相关的实现类,InputStream的用法(mark和reset方法),spring2.0对基于xml schema的XML语法的支持,如何处理配置资源;spring1.x 和 spring2.x 的变更有哪些等,由于我自己对这些也不怎么熟悉,本文都是自己的片面理解,仅供参考。

    2.  获取底层资源时,优先使用ClassPathResource、FileSystemResource、ServletContextResource等

    3. 在项目中使用时,优先使用ApplicationContext,ApplicationContext 拥有XmlBeanFactory的所有功能,XmlBeanFactory已经是一个不建议使用的东西, 源码中已经被打上了@Deprecated标识, 以后的版本可能会被去掉。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值