0
点赞
收藏
分享

微信扫一扫

《Spring》第九篇 XML配置文件的解析过程

肉肉七七 2022-02-12 阅读 35
spring
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,有两个实现类BC,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();
		}
	}
7. 当XML配置资源文件中的所有标签解析完后, 每一个<bean>标签对应一个Bean对象, 对应容器中的一个BeanDefition,此时的容器中存在多个BeanDefition, 然后对每一个BeanDefition创建对应的Bean实例对象, 就会进入Bean的生命周期环节。
举报

相关推荐

0 条评论