Spring IOC - BeanDefinition注册

上次分享中,提到了 Spring 把查找出来的资源加载解析成为 Document 对象,供后面的 BeanDefinition 注册使用。本次分享,就来看看 BeanDefinition 是如何注册的,以及他注册到什么地方。

 

1. 分析源码了解 BeanDefinition 的注册过程

  

1.1org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建 BeanDefinitionDocumentReader,默认创建是 DefaultBeanDefinitionDocumentReader
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	documentReader.setEnvironment(this.getEnvironment());
	int countBefore = getRegistry().getBeanDefinitionCount();
	// resource 其实就是 ApplicationContext 实例
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	return getRegistry().getBeanDefinitionCount() - countBefore;
}
// 你可以通过配置 documentReaderClass 属性来改变 BeanDefinitionDocumentReader 的实例
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
	return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
// 创建 XmlReaderContext 传递到 documentReader 上
protected XmlReaderContext createReaderContext(Resource resource) {
	if (this.namespaceHandlerResolver == null) {
		this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
	}
	// resouce 等信息设置到上下文中
	return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
			this.sourceExtractor, this, this.namespaceHandlerResolver);
}

通过上面的方法,初始准备了解析 Bean 定义需要的解析环境,把相关的解析需用的参数设置完成。其中有很重要的命名空间处理器(NamespaceHandlerResolver)会在后续的分享中讲解。

 

 

1.2 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

 

doRegisterBeanDefinitions方法是被registerBeanDefinitions调用的,完成注册工作转交给BeanDefinitionParserDelegate 的工作。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	 // 设置上下文
	this.readerContext = readerContext;

	logger.debug("Loading bean definitions");
	// 获取 Document 根元素
	Element root = doc.getDocumentElement();

	// 执行注册操作
	doRegisterBeanDefinitions(root);
}

protected void doRegisterBeanDefinitions(Element root) {
	String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
	// 解析 profile 属性,判断是否需要注册此资源文件(spring3.0的新属性)
	if (StringUtils.hasText(profileSpec)) {
		Assert.state(this.environment != null, "environment property must not be null");
		String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		if (!this.environment.acceptsProfiles(specifiedProfiles)) {
			return;
		}
	}
	// 创建 BeanDefinitionParserDelegate,真正做解析工作的类实例
	// any nested <beans> elements will cause recursion in this method. In
	// order to propagate and preserve <beans> default-* attributes correctly,
	// keep track of the current (parent) delegate, which may be null. Create
	// the new (child) delegate with a reference to the parent for fallback purposes,
	// then ultimately reset this.delegate back to its original (parent) reference.
	// this behavior emulates a stack of delegates without actually necessitating one.
	BeanDefinitionParserDelegate parent = this.delegate;
	// 在实例化的同时,需要检查 Bean 上面的初始化配置是否正确
	// BeanDefinitionParserDelegate#initDefaultsElement, BeanDefinitionParserDelegate)
	this.delegate = createHelper(readerContext, root, parent);

	preProcessXml(root);
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);

	this.delegate = parent;
}

 

1.3 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

// 解析 Bean 定义的根节点,包含 import, alias, bean。
// Root 是解析的DOM根节点
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	// 如果root 节点的namespace是空,或者是 http://www.springframework.org/schema/beans 
	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;
				// 和root节点的判断方式相同,如果是默认命名空间
				if (delegate.isDefaultNamespace(ele)) {
					parseDefaultElement(ele, delegate);
				}
				// 非默认命名空间,自定义节点
				else {
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	// 其他情况,说明是自定义节点
	else {
		delegate.parseCustomElement(root);
	}
}

 先看看默认命名空间的元素解析的入口:

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 元素,递归解析
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}

 深入看看里面的具体实现,如果不关心这个里面的细节可以选择跳过。

/**
 * import 元素解析,根据配置的 resource 属性,再一次从资源查找入手,进行导入 bean 定义解析
 * Parse an "import" element and load the bean definitions
 * from the given resource into the bean factory.
 */
protected void importBeanDefinitionResource(Element ele) {
	// 获取 resource 属性
	String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
	if (!StringUtils.hasText(location)) {
		getReaderContext().error("Resource location must not be empty", ele);
		return;
	}

	// Resolve system properties: e.g. "${user.dir}"
	// 处理系统属性
	location = environment.resolveRequiredPlaceholders(location);

	// 记录真实的资源(用表达式配置的资源可能出现多个)
	Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

	// 判断是否是绝对路径
	// Discover whether the location is an absolute or relative URI 
	boolean absoluteLocation = false;
	try {
		absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
	}
	catch (URISyntaxException ex) {
		// cannot convert to an URI, considering the location relative
		// unless it is the well-known Spring prefix "classpath*:"
	}

	// Absolute or relative?
	// 如果是绝对路径,直接解析对应绝对路径的 Bean 定义资源就可以了
	if (absoluteLocation) {
		try {
			// 这个方法在前面资源装载中已经分享过, AbstractBeanDefinitionReader#loadBeanDefinitions(String, Set<Resource>) 
			int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
			if (logger.isDebugEnabled()) {
				logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
			}
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error(
					"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
		}
	}
	// 相对路径
	else {
		// No URL -> considering resource location as relative to the current file.
		try {
			int importCount;
			// 如果资源支持创建相对路径,则创建。
			Resource relativeResource = getReaderContext().getResource().createRelative(location);
			// 判断如果资源存在,进行解析
			if (relativeResource.exists()) {
				importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
				actualResources.add(relativeResource);
			}
			// 如果不存在,在拼一下绝对路径,进行解析。如果此时无法查找到资源,就会抛异常了
			else {
				String baseLocation = getReaderContext().getResource().getURL().toString();
				importCount = getReaderContext().getReader().loadBeanDefinitions(
						StringUtils.applyRelativePath(baseLocation, location), actualResources);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
			}
		}
		catch (IOException ex) {
			getReaderContext().error("Failed to resolve current resource location", ele, ex);
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
					ele, ex);
		}
	}
	Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
	// 通知 import 元素已经解析完毕
	getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

/**
 * 处理别名,或者说叫注册别名
 * Process the given alias element, registering the alias with the registry.
 */
protected void processAliasRegistration(Element ele) {
	String name = ele.getAttribute(NAME_ATTRIBUTE);
	String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
	boolean valid = true;
	if (!StringUtils.hasText(name)) {
		getReaderContext().error("Name must not be empty", ele);
		valid = false;
	}
	if (!StringUtils.hasText(alias)) {
		getReaderContext().error("Alias must not be empty", ele);
		valid = false;
	}
	if (valid) {
		try {
			// 注册到别名Map中,后面会再次提到此处的使用
			getReaderContext().getRegistry().registerAlias(name, alias);
		}
		catch (Exception ex) {
			getReaderContext().error("Failed to register alias '" + alias +
					"' for bean with name '" + name + "'", ele, ex);
		}
		getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
	}
}

/**
 * 处理 bean 元素,本身不处理,交给了 BeanDefinitionParserDelegate 
 * Process the given bean element, parsing the bean definition
 * and registering it with the registry.
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// 解析Bean定义,存放在 BeanDefinitionHolder 中
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		// 判断是否需要装饰 BeanDefinition,如果需要装饰,返回装饰后的 Bean 定义
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance.
			// 注册 Bean 到 Factory
			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));
	}
}

BeanDefinitionHolder

BeanDefinitionHolder  不仅包含了 BeanDefinition 信息,同时记录了对应的Bean的名称以及别名信息。

 

1.4 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element, BeanDefinition)

BeanDefinitionParserDelegate解析 Bean 定义的地方。

 

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
	// id 属性
	String id = ele.getAttribute(ID_ATTRIBUTE);
	// name 属性,可以用“,;”分割,得到别名定义
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

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

	// bean 的唯一名称是定义的 id 属性,不是name
	// 如果当没有定义 id 属性时,从定义的别名中取得一个
	String beanName = id;
	if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
		beanName = aliases.remove(0);
		if (logger.isDebugEnabled()) {
			logger.debug("No XML 'id' specified - using '" + beanName +
					"' as bean name and " + aliases + " as aliases");
		}
	}

	// 如果不是嵌套bean,验证 beanName 唯一性
	if (containingBean == null) {
		checkNameUniqueness(beanName, aliases, ele);
	}

	// 解析 bean 属性值以及子元素标签
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
		if (!StringUtils.hasText(beanName)) {
			try {
				// 如果是内部 bean,创建内部 bean 名称
				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.isDebugEnabled()) {
					logger.debug("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);
		// 返回 BeanDefinitionHolder
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}

	return null;
}
  1.5 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#decorateIfRequired

如果自定义的 schema 来进行 Bean 解析,调用对应的 NameSpaceHandler 进行装饰。具体的自定义方式、方法、原理将在下次分享中分享。

private BeanDefinitionHolder decorateIfRequired(
		Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
	// 获取对应的命名空间,如果不是 Spring 定义的默认命名空间,进行装饰处理
	String namespaceUri = getNamespaceURI(node);
	if (!isDefaultNamespace(namespaceUri)) {
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler != null) {
			return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
		}
		else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
			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;
}

 

 

1.6 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(Element, BeanDefinition)

 

解析自定义 schema 中的 bean 元素,进行自定义解析

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
	String namespaceUri = getNamespaceURI(ele);
	// 获取对应的命名空间解析处理器来进行解析处理
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
	// 解析的实现中选择是否需要把bean定义注册到 Factory 中
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

 

1.7 org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition

 

此法静态方法,负责把解析好的 Bean 定义注册到容器中

public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {

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

	// Register aliases for bean name, if any.
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String aliase : aliases) {
			registry.registerAlias(beanName, aliase);
		}
	}
}

 

 

一般的容器都会继承 DefaultListableBeanFactory,我们看看 Bean 注册是怎么实现的:

org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
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 {
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}

	synchronized (this.beanDefinitionMap) {
		// 所有解析好的 Bean 定义都会放在 beanDefinitionMap 中
		Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		// 如果可以通过 Bean 名称获取到 Bean 定义,那么需要判断是否允许覆盖
		if (oldBeanDefinition != null) {
			if (!this.allowBeanDefinitionOverriding) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
		}
		else {
			this.beanDefinitionNames.add(beanName);
			this.frozenBeanDefinitionNames = null;
		}
		// 注册 Bean 定义到 beanDefinitionMap 中
		this.beanDefinitionMap.put(beanName, beanDefinition);

		resetBeanDefinition(beanName);
	}
}

 

综上,所有解析好的 Bean 定义被封装在 BeanDefinitionHolder 中,让后经过各种判断最终放在了 DefaultListableBeanFactory 的 benDefinitionMap 中。至此,Bean 解析和注册完成,为后续的依赖注入提供了数据基础。下次分享还是要深化这个解析过程,讲述自定义 schema 解析Bean过程。

 

OK,今天就到这里吧。

猜你喜欢

转载自bodu-li.iteye.com/blog/2145267