1. 当启动ClassPathXmlApplicationContext容器时, 为指定的BeanFactory创建一个XmlBeanDefinitionReader
阅读器。
源码: 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.
* <p>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<Resource> 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;
}
}
3. 再调用XmlBeanDefinitionReader的loadBeanDefinitions
(Resource resource)方法,将资源Resourced进一步封装为EncodedResource
对象,查看源码可以发现里面增加了对字符集
和编码
的封装,从命名上来看也可以体现出来,将资源封装完成后,就调用重载的同名方法loadBeanDefinitions()
来加载资源。
源码: 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<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 {
// 获取资源文件流
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文档, 尤其是读取<beans>
标签元素然后封装成Element
,通过BeanDefinitionParserDelegate
解析器来解析Element
中的所有根级别
的子标签, 尤其是<import>,<alias>,<bean>
源码: 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中解析出来的<beans>标签信息
if (delegate.isDefaultNamespace(root)) {
// nl 是<beans>标签中子标签, 常见的子标签有<bean>,<import>,<alias>
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. 获取到<beans>
标签中所有根子标签后,然后分别解析每个根子标签中的元素信息
<import>
标签: 主要是在一个xml配置文件中导入另一个xml文件,核心元素 resource<alias>
标签: 主要是给Bean配置别名,核心元素name为Bean的名称,alias为配置的别名<bean>
标签: 定义Bean, 一个<bean>
标签对应一个实例Bean
此处重点描述解析<bean>
标签中子元素, 见源码BeanDefinitionParserDelegate.parseBeanDefinitionElement()
/**
* 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)) { // <bean> 标签中是否存在class元素
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null; // <bean> 标签中是否存在parent元素, 是否有有父级类
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建一个GenericBeanDefinition,并赋值parentName和beanClass属性
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// ★ 核心: 解析每个<bean>标签中的属性元素
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 如果ele中存在description元素,那么将此元素的信息做为BeanDefinition的description属性信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// ★ 核心: 解析<bean>标签的子标签<constructor-arg>, 定义构造函数来实现注入
parseConstructorArgElements(ele, bd);
// ★ 核心: 解析<bean>标签下的<property>属性标签信息
parsePropertyElements(ele, bd);
// ★ 核心: 解析<bean>标签下的<qualifier>属性标签信息
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
} catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
} catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
} catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
} finally {
this.parseState.pop();
}
return null;
}
- 6-1 解析
<bean>
标签中的元素信息, 见源码:BeanDefinitionParserDelegate.parseBeanDefinitionAttributes()
/**
* 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) {
// <bean>标签中是否存在singleton元素, singleton元素已经被scope替代了
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
// <bean>标签中是否存在scope元素, scope可定义当前Bean是原型的还是单例的,不配置,默认单例
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
} else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
// <bean>标签中是否存在abstract元素,可定义当前Bean是否为抽象类,默认false
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// <bean>标签中是否存在lazy-init元素,可定义当前Bean是否懒加载,默认false
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// <bean>标签中是否存在autowire元素,可定义当前Bean的属性注入模型,byName / byType
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// <bean>标签中是否存在depends-on元素,可定义当前Bean是否依赖了其他Bean, 允许依赖多个, 用",;" 分割
// 如果依赖了其他Bean,那么必须等其他Bean创建完成,才可以继续创建当前Bean
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// <bean>标签中是否存在autowire-candidate元素, 默认是default
// 如果一个接口A,有两个实现类B和C, 类D依赖了接口A, 此时程序就不知道去走哪个实现类的逻辑,
// 如果实现类B上添加autowire-candidate并设置为false,表示实现类B不参与注入
// 那么程序就会直接走实现类C的逻辑
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
} else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// <bean>标签中是否存在primary元素,默认值是true
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// <bean>标签中是否存在init-method元素, 可定义当前Bean的初始化方法
// 没有设置,就是用默认的初始化方法
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
} else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// <bean>标签中是否存在destroy-method元素,可定义当前Bean的销毁方法
// 将当前Bean打上销毁标签,当容器销毁时,会执行这个销毁的方法中的逻辑
// 没有设置,就是用默认的初始化方法
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
} else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// <bean>标签中是否存在factory-method元素,可定义BeanFactory创建指定Bean时用到的方法
// <bean>标签中是否存在factory-bean元素,可以定义当前Bean是由哪个BeanFactory来创建
// 当使用BeanFactory去创建Bean对象时,如果创建Bean的方法是非静态的,那么就必须先创建BeanFactory实例,设置工厂Bean创建指定对象的方法
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
- 6-2 解析
<bean>
标签的子标签<constructor-arg>
① 定义构造函数来实现注入, 可能会存在多个<constructor-arg>
标签, 因为实例Bean的构造函数参数可能会存在多个, 每一个<constructor-arg>
标签代表构造函数的一个参数,可用index属性来指定参数的注入顺序。然后循环解析<constructor-arg>
标签, 调用BeanDefinitionParserDelegate.parseConstructorArgElement()
方法去解析每一个<constructor-arg>
标签中元素信息。
① 如果<constructor-arg>
标签中使用了index来指定构造函数参数的注入顺序,那么index值就是对应构造函数参数的顺序; 如果没有使用index来指定注入顺序,那么<constructor-arg>
标签的顺序就是构造函数参数的注入顺序; 标签中指定的参数值一般用value
或者ref
元素表示, 将参数值封装成ConstructorArgumentValues
对象,并存储到对应的BeanDefinition
中,且指定参数的索引顺序
/**
* Parse a constructor-arg element.
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// <constructor-arg> 标签中 index属性,表示造函数的参数的顺序位置
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// <constructor-arg> 标签中 type属性, 表示造函数的参数类型
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// <constructor-arg> 标签中 name属性, 表示造函数的参数名称
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 指定构造函数参数的注入顺序,那么index值就是对应构造函数参数的顺序
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
} else {
try {
// 记录每个构造函数注入点
this.parseState.push(new ConstructorArgumentEntry(index));
// 获取<constructor-arg>标签中ref或者value元素的值,并封装成一个ConstructorArgumentValues对象
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
} else {
// 在BeanDefinition中ConstructorArgumentValues属性中,给构造函数的参数打上索引标签
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
} finally {
this.parseState.pop();
}
}
} catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
// 未指定构造函数参数的注入顺序, 那么<constructor-arg>的顺序就是构造函数参数的顺序
else {
try {
// 记录每个构造函数注入点
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
} finally {
this.parseState.pop();
}
}
}
- 6-3 解析
<bean>
标签的子标签<property>
① 此方式是直接使用<property>
标签对实例Bean中的属性直接赋值, 实例Bean中的属性可能存在多个,那么就有多个<property>
标签, 需要循环去调用BeanDefinitionParserDelegate.parsePropertyElement()
方法去解析每一个<property>
标签中元素信息
② 通过标签中name元素去判断propertyValueList
中是否已经存在属性值了,如果存在,表示通过spring的扩展点手动给属性赋过值了,无需覆盖赋值
③ 如果属性没有赋过值,那么将整个<property>
标签封装成独立的PropertyValue
对象,然后封装到BeanDefinition中
/**
* 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();
}
}
- 6-4 解析
<bean>
标签下的<qualifier>
属性标签信息
①<qualifier>
标签可能会定义多个,因为一个Bean中可能存在多个实例属性,每个实例属性都可能需要<qualifier>
标签来定义。通过BeanDefinitionParserDelegate.parseQualifierElements()
来解析每一个<qualifier>
标签。
③ 通过<qualifier>
标签中的type属性,封装成一个AutowireCandidateQualifier对象
/**
* Parse a qualifier element.
*/
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
// 获取<qualifier> 标签中 type属性, 表示属性的类型
String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
if (!StringUtils.hasLength(typeName)) {
error("Tag 'qualifier' must have a 'type' attribute", ele);
return;
}
// 记录注入点
this.parseState.push(new QualifierEntry(typeName));
try {
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(extractSource(ele));
// 获取<qualifier> 标签中 value属性, 表示属性的类型
String value = ele.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(value)) {
qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
}
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 获取<qualifier> 标签中的子标签<attribute>
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
Element attributeEle = (Element) node;
// 获取子标签<attribute>中的key和value元素
String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(extractSource(attributeEle)); // 定义所属
qualifier.addMetadataAttribute(attribute);
} else {
error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
return;
}
}
}
bd.addQualifier(qualifier);
} finally {
this.parseState.pop();
}
}