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
方法进行验证模式检测时,首先会检查Resource
的idOpen()
,如果返回的是 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 。
而相关的实现有很多, 如下列出的:
* 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();
}