spring加载xml文件异常解决方案记录

895 阅读2分钟

Spring版本: 5.3.x

问题描述

在测试Spring Bean工厂加载XML文件的时候,报出如下异常:

Passed-in Resource [InputStream 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.
org.springframework.beans.factory.BeanDefinitionStoreException: Passed-in Resource [InputStream 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:468)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.getValidationModeForResource(XmlBeanDefinitionReader.java:449)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadDocument(XmlBeanDefinitionReader.java:433)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:338)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReaderTests.testSimpleBeanLoad(XmlBeanDefinitionReaderTests.java:70)

先贴出源码:

@Test
public void testSimpleBeanLoad() {
   //新版本XML的Bean工厂
   DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
   //获取配置文件
   Resource resource = new InputStreamResource(getClass().getResourceAsStream("test.xml"));
   //加载配置文件到Bean工厂
   new XmlBeanDefinitionReader(factory).loadBeanDefinitions(resource);
   //获取其中的一个Bean配置
   TestBean bean = factory.getBean("rod", TestBean.class);
   assertThat(bean).isNotNull();
   assertThat(bean.getName()).isNotNull();
}

问题分析

  • 直接原因: org.springframework.beans.factory.xml.XmlBeanDefinitionReader#detectValidationMode方法进行验证模式检测时,首先会检查ResourceidOpen(),如果返回的是 true, 则会抛出上述的BeanDefinitionStoreException异常。
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.");
    }
    //...
}

  • 根本原因 isOpen()的值是Resource实现本身写定的,我们使用的InputStreamResource刚好定义的是true, 故而报出此异常。
public boolean isOpen() {
   return true;
}

解决方案

其实问题的解决方案在InputStreamResource中已经注释出来了。

给定InputStream Resource实现。
仅当没有其他特定的Resource实现适用时才应使用。 
特别是,在可能的情况下,更喜欢ByteArrayResource或任何基于文件的Resource实现。
与其他Resource实现相反,这是一个已经打开的资源的描述符 - 因此从isOpen()返回true 。 
如果需要将资源描述符保留在某处,或者需要多次从流中读取,请不要使用InputStreamResource 。

image.png

而相关的实现有很多, 如下列出的:

* WritableResource
* ContextResource
* UrlResource
* FileUrlResource
* FileSystemResource
* ClassPathResource
* ByteArrayResource
* InputStreamResource

最终的代码:

@Test
public void testSimpleBeanLoad() {
   //新版本XML的Bean工厂
   DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
   //获取配置文件
   Resource resource = new FileSystemResource(getClass().getResource("test.xml").getPath());
   //加载配置文件到Bean工厂
   new XmlBeanDefinitionReader(factory).loadBeanDefinitions(resource);
   //获取其中的一个Bean配置
   TestBean bean = factory.getBean("multiAliased", TestBean.class);
   assertThat(bean).isNotNull();
   assertThat(bean.getName()).isNotNull();
}