Spring如何加载配置文件到应用程序的
# Spring如何加载配置文件到应用程序
## 加载Xml文件配置,获取对象
>i xml文件
```xml
<?xml version="1.0" encoding="UTF-8"?>
<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="user" class="com.ibli.javaBase.reflection.User">
<property name="age" value="12"/>
<property name="name" value="gaolei"/>
<property name="sex" value="male"/>
</bean>
</beans>
```
>i 测试类
```java
public class IocDemo {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-ioc.xml");
User user = (User) ac.getBean("user");
System.out.println(user);
}
}
```
## Spring 加载Xml文件流程
>w 首先猜想一下宏观的流程
<img src="https://oscimg.oschina.net/oscnet/up-6aaea943c32e32e44e672c1850975bd40ae.png">
我们可以大体猜想流程是什么样的,如下👇
<img src="https://oscimg.oschina.net/oscnet/up-992d3c9c618a021852b873f08b756824901.png">
接下来debug源码看一下具体流程:
>i ClassPathXmlApplicationContext调用refresh方法
```java
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
// Spring 启动入口
this.refresh();
}
}
```
Spring 启动入口 this.refresh(); 👆
>i 调用AbstractRefreshableApplicationContext下的refreshBeanFactory
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
```java
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
// 从这里进入下一步 👇
this.loadBeanDefinitions(beanFactory);
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
```
<font color=red>关键方法是this.loadBeanDefinitions(beanFactory);</font>
>i 找到XmlBeanDefinitionReader 这是读取配置的关键所在
关键对象 <font color=red>XmlBeanDefinitionReader</font> 这个在 「梳理Spring启动脉络」中提到了,Spring提供的抽象接口!
```java
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化beanDefinitionReader对象
this.initBeanDefinitionReader(beanDefinitionReader);
// 加载配置文件 获得BeanDefinitions
this.loadBeanDefinitions(beanDefinitionReader);
}
```
>i 继续调用 loadBeanDefinitions 这个有很多重载方法,一直点下去就行!
```java
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = this.getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = this.getConfigLocations();
//spring-ioc.xml
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
```
configLocations 就是我们Xml配置文件的路径
>i 接下来一直调用loadBeanDefinitions方法 直到这一步 👇
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
```java
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
Throwable var4 = null;
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
var6 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} catch (Throwable var24) {
var4 = var24;
throw var24;
} finally {
if (inputStream != null) {
if (var4 != null) {
try {
inputStream.close();
} catch (Throwable var23) {
var4.addSuppressed(var23);
}
} else {
inputStream.close();
}
}
}
}
```
这里看到 <font color=red >InputStream</font> 很明显,这里是通过IO流读取制定位置的文件的 !
>i 获取到文件输入流之后,将输入流转换成Document文件去解析
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException
```java
// 转换成Document的关键方法
Document doc = this.doLoadDocument(inputSource, resource);
```
>i 调用doRegisterBeanDefinitions方法
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
调用parseBeanDefinitions方法去解析数据
>i 调用DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法 来解析Element
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
```java
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
```
>i 调用parseDefaultElement方法
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement
```java
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
```
这里看到`if (delegate.nodeNameEquals(ele, "bean"))` 会不会很兴奋呢,接下来就是解析的方法了👇
>i 跳转到 processBeanDefinition(ele, delegate);
```java
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 是的 就是这个方法了 👉
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
```
>i parseBeanDefinitionElement 将元素数据解析到beanDefinition
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
```java
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute("id");
String nameAttr = ele.getAttribute("name");
List<String> aliases = new ArrayList();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(id) && !aliases.isEmpty()) {
beanName = (String)aliases.remove(0);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
this.checkNameUniqueness(beanName, aliases, ele);
}
// 将element数据最终转换成一个beanDefinition对象 是不是很惊奇 哈哈哈
👉 AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
}
} catch (Exception var9) {
this.error(var9.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} else {
return null;
}
}
```