spring源码06: bean标签 解析注册

先回顾一下上一节讲到默认名称空间的解析,默认标签分为4中:importaliasbeanbeans
本节将对bean标签的解析、修饰、注册进行讲解
当前阶段对应是流程图的 DOM树 -> beanDefinition -> beanDefinitionHolder -> register

// DefaultBeanDefinitionDocumentReader.java
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		// 解析import标签
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		// 解析alias标签
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		// 解析bean标签
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		// 解析beans标签,解析该标签实际是递归调用doRegisterBeanDefinitions,前面提到的parent
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

DOM树 -> beanDefinition

处理bean标签

processBeanDefinition方法分成了3个步骤,1.解析并生成beanDefinition; 2.修饰beanDefinition; 3.注册beanDefinition

// DefaultBeanDefinitionDocumentReader.java
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		/**
		 * 1.解析并生产BeanDefinitionHolder
		 * BeanDefinitionHolder跟beanDefinition没有本质区别,只不过更方便获取beanName、aliases
		 * BeanDefinitionHolder = beanName + aliases + beanDefinition
		 */
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			/**
			 * 2.字面意思是如果需要的话修饰beanDefinition,实际是为了处理默认标签下包含的自定义标签
			 * 因此可以看到ele参数一样需要被传递进去,而不是单纯的操作bdHolder
			 * <bean id="car" class="Car">
			 *     <mybean:user username="chaitou">
			 * </bean>
			 */
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
				// 3.注册的核心逻辑,getReaderContext().getRegistry()中保存着所有注册过的beanDefinition
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

1. 解析并生成beanDefinition

①提取id,name,aliases
②剩下的所有属性解析交给parseBeanDefinitionElement处理,并把属性封装到GenericBeanDefinition实例中
③如果没有beanName,用默认规则生成一个
④将name,aliases,beanDefinition封装成beanDefinitionHolder并返回

// BeanDefinitionParserDelegate.java
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		// 1. 解析ID
		String id = ele.getAttribute(ID_ATTRIBUTE);
		// 解析name
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		// 解析别名
		List<String> aliases = new ArrayList<>();
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			// 如果没有Id,则用第一个别名代替
			beanName = aliases.remove(0);
			if (logger.isTraceEnabled()) {
				logger.trace("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}

		// 通过usedNames判断beanName跟Aliases有没有被其他bean使用,如果被占用则报错,未被引用则将当前beanName添加到userNames中,用于后续判断
		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}

		// 2. 解析各种属性及标签,并分装到GenericBeanDefinition实例中
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			// 3. 如果检测到beanName为空,则使用默认规则生产一个beanName
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						beanName = this.readerContext.generateBeanName(beanDefinition);
						// Register an alias for the plain bean class name, if still possible,
						// if the generator returned the class name plus a suffix.
						// This is expected for Spring 1.2/2.0 backwards compatibility.
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isTraceEnabled()) {
						logger.trace("Neither XML 'id' nor 'name' specified - " +
								"using generated bean name [" + beanName + "]");
					}
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			// 别名数组
			String[] aliasesArray = StringUtils.toStringArray(aliases);
			// 4. 返回beanDefinitionHolder,实际仅仅比beanDefinition多带了beanName,aliases属性,没有什么太大区别,方便获取
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}

继续跟进第二点parseBeanDefinitionElement

// BeanDefinitionParserDelegate.java
public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, @Nullable BeanDefinition containingBean) {
		// 标识当前bean正在解析状态
		this.parseState.push(new BeanEntry(beanName));

		// 解析class属性
		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}

		// 解析parent属性
		String parent = null;
		if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
			parent = ele.getAttribute(PARENT_ATTRIBUTE);
		}

		try {
			// 创建BeanDefinition,实际创建GenericBeanDefinition,BeanDefinition的集大成者,就是该会的方法都会的那种
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			// 解析各种属性
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
			
			// 解析元数据
			parseMetaElements(ele, bd);
			// 解析lookup-method属性
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			// 解析replaced-method属性
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

			// 解析构造函数参数
			parseConstructorArgElements(ele, bd);
			// 解析property元素
			parsePropertyElements(ele, bd);
			// 解析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;
	}

parseBeanDefinitionAttributes基本上都是千篇一律的硬编码的解析,这里就不看了。
我们阅读下beanDefinition的创建,以及挑一个解析lookup-method属性(parseLookupOverrideSubElements

// BeanDefinitionParserDelegate.java
	public static AbstractBeanDefinition createBeanDefinition(
			@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

		// BeanDefinition的一站式服务类,也就是集齐所有实现AbstractBeanDefinition的功能
		GenericBeanDefinition bd = new GenericBeanDefinition();
		bd.setParentName(parentName);
		if (className != null) {
			// 如果classLoader不为空,则使用传入的classLoader加载类对象,否则只记录ClassName
			if (classLoader != null) {
				bd.setBeanClass(ClassUtils.forName(className, classLoader));
			}
			else {
				bd.setBeanClassName(className);
			}
		}
		return bd;
	}
// BeanDefinitionParserDelegate.java
	public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
		NodeList nl = beanEle.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			/**
			 * 解析<lookup-method> 标签
			 * 假设标签为<lookup-method name="getBean" bean="cat">
			 */
			if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
				Element ele = (Element) node;
				// 解析name属性,即getBean
				String methodName = ele.getAttribute(NAME_ATTRIBUTE);
				// 解析bean属性,即cat
				String beanRef = ele.getAttribute(BEAN_ELEMENT);
				// bean跟name将唯一定位一个方法的返回值将被哪一个bean替换
				LookupOverride override = new LookupOverride(methodName, beanRef);
				override.setSource(extractSource(ele));
				overrides.addOverride(override);
			}
		}
	}

2. 修饰beanDefinition

/**
 * 2.字面意思是如果需要的话修饰beanDefinition,实际是为了处理默认标签下包含的自定义标签
 * 因此可以看到ele参数一样需要被传递进去,而不是单纯的操作bdHolder
 * <bean id="car" class="Car">
 *     <mybean:user username="chaitou">
 * </bean>
 */
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

解析bean的代码实在太多了,已经不知道执行到哪里,重新拉回来,目前执行到步骤2。当代码执行到这里,原始的beanDefinition已经被创建出来了,但是他并不完美,当bean里面又嵌套了自定义标签时,默认名称空间的delegate是无法解析的,所以还需要一步装饰bdHolder,用来解析自定义标签

// BeanDefinitionParserDelegate.java
	public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
			Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

		BeanDefinitionHolder finalDefinition = originalDef;

		// Decorate based on custom attributes first.
		// 便利所有属性,如果有需要的话装饰属性
		NamedNodeMap attributes = ele.getAttributes();
		for (int i = 0; i < attributes.getLength(); i++) {
			Node node = attributes.item(i);
			finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
		}

		// Decorate based on custom nested elements.
		// 便利所有元素,如果有需要的话装饰元素
		NodeList children = ele.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node node = children.item(i);
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
			}
		}
		return finalDefinition;
	}
// BeanDefinitionParserDelegate.java
	public BeanDefinitionHolder decorateIfRequired(
			Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

		// 获取名称空间
		String namespaceUri = getNamespaceURI(node);
		// 默认名称空间的节点已经解析过,只有自定义名称空间才需要进行装饰
		// 默认名称空间怎么判断?等于"http://www.springframework.org/schema/beans"就是默认名称空间
		if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
			// 获取处理器,自定义名称空间需要自己override对应的处理器
			NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
			// 存在对应的处理器
			if (handler != null) {
				// 使用自定义处理器的decorate进行修饰,decorate同样是留给用户override的
				BeanDefinitionHolder decorated =
						handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
				if (decorated != null) {
					return decorated;
				}
			}
			else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
				error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
			}
			else {
				// A custom namespace, not to be handled by Spring - maybe "xml:...".
				if (logger.isDebugEnabled()) {
					logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
				}
			}
		}
		return originalDef;
	}

3. 注册BeanDefinition

BeanDefinition被封装成BeanDefinitionHolder后,又经过修饰以后,已经成为了一个究极完美体的beanDefintion了,他已经可以百分百定义一个bean了。接下去需要做的事情就是将这些究极体的beanDefiniton一起丢到一个Map中,好让第二阶段getBean时能够快速准确的创建出用户需要的bean

该部分的关键在2个Map:

  • beanDefinitionMap: 用于保存所有的beanDefinition(key = beanName,value = beanDefinition)
  • aliasMap: 用于保存别名(key = alias,value = beanName)

假设有一个beanDefinition beanName = "myBean" alias = "IBean"(以下是伪代码,只是为了举例说明)
当我们需要通过alias = "IBean"查找该beanDefinition时,spring会beanName = aliasMap.get("IBean"),再用beanDefinition = beanDefinitionMap.get(beanName)获取到对应的beanDefinition

// 3.注册的核心逻辑,getReaderContext().getRegistry()中保存着所有注册过的beanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
// BeanDefinitionReaderUtils.java
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		// 注册key = beanName, value = beanDefinition
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		// 注册别名key = alias, value = beanName
		// 方便通过别名找到beanName,再通过beanName找到对于的beanDefinition
		String[] aliases = definitionHolder.getAliases();//- 注册别名
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}
  1. 注册beanDefinition

其实注册的操作很简单,关键语句就2句:

// 注册,实际就是加入map中,这就是代码的核心
this.beanDefinitionMap.put(beanName, beanDefinition);
// 该list保存着所有已经注册的的beanName
this.beanDefinitionNames.add(beanName);

但是spring还是做了大量的工作:
① 做注册前校验
② 如果beanName已经存在,判断是否能覆盖,当然还做了很多日志log
③ 如果不存在直接注册

// DefaultListableBeanFactory.java
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				// 注册前做一次校验,主要针对methodOverrides,检测看看是否存在这个方法或是否有冲突
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		// 获取看看当前beanName是不是已经存在,不存在的直接注册,存在的看看允不允许覆盖
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		// 检测当前beanName是否已经存在
		if (existingDefinition != null) {
			// 已经存在,又不允许覆盖则跑出异常
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				...
			}
			// 覆盖
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			// 检测当前程序处于那个阶段,解析注册阶段?还是已经有Bean被初始化创建阶段?
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				/**
				 * 这段代码是4.2之后才出现的
				 * 注释的意思是:程序已经有bean被实例化,也就是已经过了启动注册阶段,为了list的稳定迭代,无论如何不允许修改beanDefinitionNames集合
				 *
				 * 由于ArrayList是线程不安全的,使用ArrayList.put方法需要完成2个步骤
				 * 1.插入 2.修改size的值
				 * 为了保证线程安全,spring使用updatedDefinitions来更新
				 *
				 * 如果不这么做会造成什么后果暂时未查到相关文档,待补充 TODO
				 */
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				// 仍然处在spring启动注册阶段,即未有bean被实例化
				// 注册,实际就是加入map中,这就是代码的核心
				this.beanDefinitionMap.put(beanName, beanDefinition);
				// 该list保存着所有已经注册的的beanName
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (existingDefinition != null || containsSingleton(beanName)) {
			// 重置所有beanName对应的缓存(当然只有单例情况才会缓存)
			resetBeanDefinition(beanName);
		}
	}
  1. 注册别名

核心代码也是就一句

// 注册别名 key = alias , value = beanName
this.aliasMap.put(alias, name);
// SimpleAliasRegistry
	public void registerAlias(String name, String alias) {
		Assert.hasText(name, "'name' must not be empty");
		Assert.hasText(alias, "'alias' must not be empty");
		// 加个锁保安全
		synchronized (this.aliasMap) {
			// 当别名等于beanName时,删除别名
			if (alias.equals(name)) {
				this.aliasMap.remove(alias);
				if (logger.isDebugEnabled()) {
					logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
				}
			}
			else {
				String registeredName = this.aliasMap.get(alias);
				// 注册明已经存在
				if (registeredName != null) {
					if (registeredName.equals(name)) {
						// An existing alias - no need to re-register
						return;
					}
					if (!allowAliasOverriding()) {
						// 不允许覆盖则抛出异常
						throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
								name + "': It is already registered for name '" + registeredName + "'.");
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
								registeredName + "' with new target name '" + name + "'");
					}
				}
				// 别名的循环检测,例如A->B,再来一个A->C->B是不被允许的
				checkForAliasCircle(name, alias);
				// 注册别名 key = alias , value = beanName
				this.aliasMap.put(alias, name);
				if (logger.isTraceEnabled()) {
					logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
				}
			}
		}
	}

至此已经完成了spring的第一阶段的阅读,也就是启动注册阶段。第二阶段便是拿着beanDefinition的定义,按着定义上的要求,生产出用户所需要的bean

发布了30 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chaitoudaren/article/details/104833501