XmlBeanDefinitionReader
XmlBeanDefinitionReader
是Spring Framework中的一个类,用于加载和解析XML格式的Bean定义配置文件,将配置文件中定义的Bean元数据信息提取为Spring容器内部的Bean定义对象,进而实现IOC容器的构建和管理。这类负责读取XML配置文件,解析Bean的定义信息(包括ID、类名、属性、依赖等),并将这些定义注册到Spring应用程序上下文,使我们能够方便地配置和管理应用程序中的各种Bean组件。
主要功能
- 加载XML配置文件:
XmlBeanDefinitionReader
能够加载XML格式的Spring配置文件,通常使用Resource
对象来指定配置文件的位置,如ClassPathResource
、FileSystemResource
等。 - 解析XML文件:它将XML配置文件解析为Spring容器内部数据结构,使用
DocumentLoader
来实现。这包括将XML元素和属性转化为Spring的Bean定义信息。 - 注册Bean定义:一旦
XmlBeanDefinitionReader
成功解析XML文件中的Bean定义信息,它会将这些信息注册到Spring容器的Bean工厂,以便容器能够创建和管理这些Bean实例。
案例展示
首先创建了一个Spring容器DefaultListableBeanFactory
,然后使用XmlBeanDefinitionReader
来加载和解析名为beans.xml
的XML配置文件,将其中定义的Bean元数据信息注册到容器中。随后,通过容器获取名为myBean
的Bean实例,最后将该Bean实例打印出来。这样的操作实现了Spring容器的初始化、XML配置文件的解析,以及Bean的获取和使用。
public class XmlBeanDefinitionReaderDemo {
public static void main(String[] args) {
// 创建一个DefaultListableBeanFactory作为Spring容器
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 创建XmlBeanDefinitionReader实例用于解析XML配置
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
// 加载XML配置文件
reader.loadBeanDefinitions(new ClassPathResource("beans.xml"));
// 获取并使用Bean
MyBean myBean = factory.getBean("myBean", MyBean.class);
System.out.println("myBean = " + myBean);
}
}
ClassPathResource("sample.xml")
将加载类路径下名为sample.xml
的资源文件的内容。在我们的示例配置文件中,这个资源文件定义了一个名为myBean
的 Spring Bean,该 Bean 具有一个属性message
,其值设置为Hello World
。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myBean" class="com.xcs.spring.bean.MyBean">
<property name="message" value="Hello World"/>
</bean>
</beans>
MyBean
的Java类,代表了一个简单的Java Bean。
public class MyBean {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "MyBean{" +
"message='" + message + '\'' +
'}';
}
}
源码分析
在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(resource)
方法中,又调用了loadBeanDefinitions(encodedResource)
方法,同时将resource
包装成一个EncodedResource
对象。
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(encodedResource)
方法中,首先是创建一个InputSource
对象,用于解析XML。最后调用doLoadBeanDefinitions
方法,实际的XML解析和Bean定义加载。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
// <1> 获取当前线程正在加载的 Resource 资源集合,添加当前 Resource,防止重复加载
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) { // 将当前资源加入记录中。如果已存在,抛出异常,防止循环加载同一资源出现死循环
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 创建一个 InputSource 对象,用于解析XML
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
// 如果 encodedResource 包含字符编码信息,设置 InputSource 的编码
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 调用 doLoadBeanDefinitions 方法,实际的XML解析和Bean定义加载
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
// 关闭流
inputStream.close();
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
} finally {
// <4> 从当前线程移除当前加载的 Resource 对象
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
方法中,主要用于加载XML配置文件,解析其中的Bean定义,并注册这些Bean定义到Spring容器中。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 解析XML文档
Document doc = doLoadDocument(inputSource, resource);
// 根据 Document 实例,解析出 BeanDefinition 们并注册,返回注册数量
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
// ... [代码部分省略以简化]
}
在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadDocument
方法中,主要由DocumentLoader
执行实际的XML加载和解析操作,并将解析后的Document
对象返回。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
方法中,首先创建一个BeanDefinitionDocumentReader
实例,然后调用它的registerBeanDefinitions
方法,将XML文档中的Bean定义注册到Spring容器中,最后返回成功注册的Bean数量。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
在org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
方法中,又调用doRegisterBeanDefinitions
方法,开始实际的Bean定义注册过程。
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
对于具体DocumentElement
如何加工成BeanDefinition
,可以查看BeanDefinitionDocumentReader
源码解析
与其他组件的关系
ApplicationContext
的实现类:Spring的ApplicationContext
接口有多个实现,其中一些实现类在其内部使用XmlBeanDefinitionReader
来处理XML配置文件。例如,ClassPathXmlApplicationContext
、FileSystemXmlApplicationContext
和XmlWebApplicationContext
都使用XmlBeanDefinitionReader
。- Custom XML配置加载:如果我们编写自己的Spring应用程序或自定义的Spring容器,我们可以使用
XmlBeanDefinitionReader
来实现自定义的XML配置加载逻辑。 - 单独的
XmlBeanDefinitionReader
使用:有时,我们可能需要在应用程序中手动创建XmlBeanDefinitionReader
实例,然后使用它来加载和注册Bean定义。
常见问题
- XML文件路径正确性:确保XML配置文件的路径和名称正确,相对路径或绝对路径都要检查,否则将导致文件无法找到或读取。
- XML文件格式和编码:XML文件必须符合XML语法规则,包括标签嵌套、属性格式和XML声明。同时,确保文件使用正确的字符编码,与指定的编码一致,否则可能导致解析错误或乱码。
- Bean定义的正确性:XML文件中的Bean定义必须正确,包括Bean的属性、名称、类路径等。不正确的Bean定义可能导致Spring容器初始化失败。
- 版本兼容性:确保所使用的Spring框架版本与XML配置文件中的XML schema(XSD)版本兼容。不同版本可能需要不同的XSD版本。
- 错误处理和日志记录:在出现问题时,查看Spring框架的错误日志和异常堆栈信息,以便更好地识别和解决配置问题。及时的错误处理和日志记录有助于定位问题并加快排查过程。