1. 当启动ClassPathXmlApplicationContext容器时, 为指定的BeanFactory创建一个XmlBeanDefinitionReader阅读器。在学习Spring DI依赖注入时, 虽然现在大多使用的是通过注解的形式来实现, 确实,使用注解,简单高效,灵活, 基本上完全替代了使用XML配置文件的形式。但是,个人认为一个技术的兴起继而替代另一个技术,那么除了新技术的优势之外,老技术的缺点也是有必要了解下的。
很好奇, 系统是如何读取到xml文件中的内容的,以及各种标签, 接下来借助源码,来学习下这个解析过程
注: Spring源码的版本是5.0
源码: AbstractXmlApplicationContext.loadBeanDefinitions()
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
*
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创建一个XmlBeanDefinitionReader阅读器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this); // 设置资源加载器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader); // ★ 核心: 解析资源,生成BeanDefinition
}
2. 获取一个资源加载器ResourceLoader, 来加载文件资源并且将其封装为Resourced。
源码: AbstractBeanDefinitionReader.loadBeanDefinitions()
/** * Load bean definitions from the specified resource location. *3. 再调用XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)方法,将资源Resourced进一步封装为EncodedResource对象,查看源码可以发现里面增加了对字符集和编码的封装,从命名上来看也可以体现出来,将资源封装完成后,就调用重载的同名方法loadBeanDefinitions()来加载资源。The location can also be a location pattern, provided that the * ResourceLoader of this bean definition reader is a ResourcePatternResolver. * * @param location the resource location, to be loaded with the ResourceLoader * (or ResourcePatternResolver) of this bean definition reader * @param actualResources a Set to be filled with the actual Resource objects * that have been resolved during the loading process. May be {@code null} * to indicate that the caller is not interested in those Resource objects. * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #getResourceLoader() * @see #loadBeanDefinitions(org.springframework.core.io.Resource) * @see #loadBeanDefinitions(org.springframework.core.io.Resource[]) */ public int loadBeanDefinitions(String location, @Nullable Set
actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. // 将"spring-config.xml"加载成一个资源Resource,然后解析资源文件中的数据信息 Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
源码: XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)
/**
* Load bean definitions from the specified XML file.
*
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource);
}
Set 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 {
// 获取资源文件流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 加载xml文件
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
} finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
4. 将xml文件做为一个资源文件,将其转化为SAX输入源InputSource的形式进行加载, 再使用配置的documentLoader实际加载指定的资源文件, 然后生成一个DOM document文档
源码: XmlBeanDefinitionReader.doLoadBeanDefinitions()
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
// 使用配置的documentLoader实际加载指定的资源文件, 然后生成一个DOM document文档
document doc = doLoaddocument(inputSource, resource);
// 从DOM 文档中解析出BeanDefinition
return registerBeanDefinitions(doc, resource);
} catch (BeanDefinitionStoreException ex) {
throw ex;
}
........
}
5. 获取一个BeanDefinitiondocumentReader阅读器,去解析DOM document文档, 尤其是读取源码: DefaultBeanDefinitiondocumentReader.parseBeanDefinitions()
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
*
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// root 是从spring-config.xml中解析出来的标签信息
if (delegate.isDefaultNamespace(root)) {
// nl 是标签中子标签, 常见的子标签有,,
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)) {
parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
6. 获取到6-1 解析 6-2 解析 6-3 解析 6-4 解析
此处重点描述解析 /**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) { //
/**
* Apply the attributes of the given bean element to the given bean * definition.
*
* @param ele bean declaration element
* @param beanName bean name
* @param containingBean containing bean definition
* @return a bean definition initialized according to the bean element attributes
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
//
① 定义构造函数来实现注入, 可能会存在多个
① 如果 /**
* Parse a constructor-arg element.
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
//
① 此方式是直接使用
② 通过标签中name元素去判断propertyValueList中是否已经存在属性值了,如果存在,表示通过spring的扩展点手动给属性赋过值了,无需覆盖赋值
③ 如果属性没有赋过值,那么将整个 /**
* Parse a property element.
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 获取name元素信息, name一般指Bean对象中的属性名称,规范value是必须与属性名称一致
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
// 记录每个属性的注入点
this.parseState.push(new PropertyEntry(propertyName));
try {
// 判断是否通过手动注入的方式已经给Bean中的属性赋过值了, 如果已经赋过值了,就不需要再次赋值了
// 主要是通过Spring提供的扩展点来实现的
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
// 获取属性需要注入的值, 是value或者ref元素的值,并封装成PropertyValue对象
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parsemetaElements(ele, pv);
pv.setSource(extractSource(ele));
// 将PropertyValue对象存放到MutablePropertyValues对象中的propertyValueList属性里
// 然后封装到BeanDefinition中
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();
}
}
①
③ 通过 /**
* Parse a qualifier element.
*/
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
// 获取
7. 当XML配置资源文件中的所有标签解析完后, 每一个



