您的位置:首页 > 编程语言 > Java开发

Spring源码阅读之-bean的解析与注册

2017-07-15 14:12 686 查看

Spring源码阅读之-bean的解析与注册

接上,Spring初始化,refresh ApplicationContext时,通过refreshBeanFactory将原有的beanFactory注销,并重新读取Spring配置,并在配置中读取bean的定义。

refreshBeanFactory()
如下

/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();//销毁Beans
closeBeanFactory();//关闭BeanFactory,实际这一步是将BeanFactory类型的实例变量指向null
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);//一些自定义的设置
loadBeanDefinitions(beanFactory);//加载bean的定义
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}


loadBeanDefinitions(beanFactory)
顾名思义就是加载Bean定义的主要实现,其方法签名为

org.springframework.context.support.AbstractRefreshableApplicationContext
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException,
IOException


他是一个抽象方法由具体的子类来实现,看看这个这个方法的子类实现都有那几个。



因为我是由
org.springframework.context.support.ClassPathXmlApplicationContext
这个类一路跟过来的,其实现类为AbstractXmlApplicationContext,方法实现如下:

/**
* 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 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);
}


从代码上看,其初始化了一个
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
对象,该类实现自
org.springframework.beans.factory.support.BeanDefinitionReader
,BeanDefinitionReader依名称进行ChineseEnglish翻译,其作用为Bean定义读取器。该接口约定了几个行为:



主要看loadBeanDefinitions,几个方法,从参数列表中可得知,Bean定义的来源可以是一个Resource对象,也可以是一个String类型的文件地址,该方法返回int类型,返回值为找到的Bean的个数。方法签名如下(取loadBeanDefinitions(Resource resource)说明):

/**
* Load bean definitions from the specified resource.
* @param resource the resource descriptor
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;


org.springframework.beans.factory.xml.XmlBeanDefinitionReader
继承自
org.springframework.beans.factory.support.AbstractBeanDefinitionReader
该抽象类中定义了如下几个实例变量

private final BeanDefinitionRegistry registry;//bean定义的注册器

private ResourceLoader resourceLoader; //资源加载器

private ClassLoader beanClassLoader; //类加载器

private Environment environment;//Spring环境

private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); //bean的名称生成器


其主要是registry属性,该域声明为final,子类不可继承,即子类只能定义加载Bean的方式方法,同样获取注册器和BeanFactory的方法也都声明为final,定义如下

public final BeanDefinitionRegistry getBeanFactory() {
return this.registry;
}

@Override
public final BeanDefinitionRegistry getRegistry() {
return this.registry;
}


回到初始化XmlBeanDefinitionReader的操作

//初始化时传入注册器即当前的BeanFactory,也就是说将Bean都注册到当前的bean工厂
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);
//加载Bean的定义
loadBeanDefinitions(beanDefinitionReader);


加载Bean的定义主要是在
loadBeanDefinitions(beanDefinitionReader);
方法中完成的,其内部获得初始化ApplicationContext时的配置文件路径封装Resource对象,并调用beanDefinitionReader的loadBeanDefinitions(Resource… resources)方法,其内部循环调用loadBeanDefinitions(Resource resource)方法来分别解析具体的配置文件,该方法内部调用
doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法将Resource解析为Xml DOC并调用
registerBeanDefinitions(Document doc, Resource resource)
方法,进行进一步的操作。

/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}


在该方法内部中,发现Spring又定义了一个十分可爱的Reader,其作为与BeanDefinitionReader又不相同,该BeanDefinitionDocumentReader 为bean定义文档读取器,该BeanDefinitionDocumentReader是一个接口,他定义的行为如下:

**
* SPI for parsing an XML document that contains Spring bean definitions.
* Used by {@link XmlBeanDefinitionReader} for actually parsing a DOM document.
*
* <p>Instantiated per document to parse: implementations can hold
* state in instance variables during the execution of the
* {@code registerBeanDefinitions} method — for example, global
* settings that are defined for all bean definitions in the document.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 18.12.2003
* @see XmlBeanDefinitionReader#setDocumentReaderClass
*/
public interface BeanDefinitionDocumentReader {

/**
* Read bean definitions from the given DOM document and
* register them with the registry in the given reader context.
* @param doc the DOM document 代指 Spring 的配置文件信息,通过 刚刚的BeanDefinitionReader解析Resrouce实例的过程得到。
* @param readerContext the current context of the reader 主要包含了Resource和BeanDefinitionReader,和一些事件处理
* (includes the target registry and the resource being parsed)
* @throws BeanDefinitionStoreException in case of parsing errors
*/
void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
throws BeanDefinitionStoreException;

}


他只有一个实现类
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
,所以在此实现类中观察代码

/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement(); //获得文档的根节点,即<beans>标签
doRegisterBeanDefinitions(root); //* 继续Doc的解析并注册bean的定义
}


关于 doRegisterBeanDefinitions,先看其代码:

/**
* Register each bean definition within the given root {@code <beans/>} element.
* 注册在给定的root节点中的每一个BeanDefinition
*/
protected void doRegisterBeanDefinitions(Element root) {
// 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.
//类名最后一个单词(委托)即BeanDefinitionDocumentReader本身并不具备解析XML节点的能力,而是将该操作交给BeanDefinitionParserDelegate来代为完成
BeanDefinitionParserDelegate parent = this.delegate;
//创建委托对象
this.delegate = createDelegate(getReaderContext(), root, parent);
//判断是否是默认命名空间的节点(因为是解析bean的功能,其默认的命名空间URI为‘http://www.springframework.org/schema/beans’,即只处理beans-3.0.xsd中定义的节点)
if (this.delegate.isDefaultNamespace(root)) {
// 解析 profile
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//空方法
preProcessXml(root);
//解析Bean的定义
parseBeanDefinitions(root, this.delegate);
//空方法
postProcessXml(root);

this.delegate = parent;
}


关于 doRegisterBeanDefinitions,该方法的主要作用有:创建 BeanDefinitionParserDelegate 对象,用于将 Document 的内容转成 BeanDefinition 实例,也就是上面提到的解析过程

下面开始通过遍历取得 beans 元素的所有子节点,其逻辑在
parseBeanDefinitions(root, this.delegate);
中。

/**
* 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) {
// 验证Doc文件的命名空间
if (delegate.isDefaultNamespace(root)) { //又来判断
//获得根元素(<beans>)的所有子元素
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)) {
//解析<beans>子元素的内容
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele); //否则解析自定义节点
}
}
}
}
else { //按自定义节点解析
delegate.parseCustomElement(root);
}
}


通过以上代码发现,parseBeanDefinitions在拿到了标签的子元素后,开始解析子元素标签,常见的标签有 import,alias,bean,beans 等(详见spring-beans.xsd或在线doc,或delegate类的常量)。

这里delegate.parseCustomElement(ele); 是另一个处理分支,例如aop和web开发配置文件中常见的annotion这类标签即是通过该方法解析的,暂不探究,另起炉灶说明

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { //判断是否是<import>标签
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { //判断是否是<alias>标签
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {//判断是否是<bean>标签,*重要:目前只关心这个处理方式
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {//判断是否是<beans>标签
// recurse
doRegisterBeanDefinitions(ele);
}
}


parseDefaultElement方法中判断各标签,并根据标签不同调用不同的方法解析标签(其中import即是获取import文件路径,将上边叙述的过程又走了一遍,不再表述),我们目前只关注beans->bean的解析过程

上面提到 bean 标签的具体解析工作是委托给 BeanDefinitionParserDelegate 类来完成的,其解析的代码如下

/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//将解析结果添加BeanDefinitionHolder对象,该对象存储BeanDefinition基本信息(内部持有一private final BeanDefinition beanDefinition;),具体解析过程就是一个解析element的过程,将解析结果封装到一个BeanDefinition中,并交给Holder持有
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 注册最后的装饰实例
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));
}
}


可以发现
processBeanDefinition
在完成解析取得 BeanDefinition(被添加进了 BeanDefinitionHolder ) 对象之后利用 BeanDefinitionRegistry 完成注册过程(BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());)。

BeanDefinitionHolder bdHolder 是如何返回的?其解析过程如下

/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//获得<bean>节点的Id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
//获得<bean>节点的name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

List<String> aliases = new ArrayList<String>();
//判断name属性是否为NULL
if (StringUtils.hasLength(nameAttr)) {
这个函数用于将给定的字符串按照给定的分隔符分隔成字符串数组,这里就是 把nameAttr按照“,;”分隔开
//这个函数用于将给定的字符串按照给定的分隔符分隔成字符串数组,这里就是 把nameAttr按照“,;”分隔开
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 把结果添加进别名集合
aliases.addAll(Arrays.asList(nameArr));
}
//beanName默认为id属性值
String beanName = id;
//如果id属性为空,且别名集合中不为空
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
//取别名集合的第一个元素当作 id ,并将其从别名集合当中移除
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) { //日志
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}

if (containingBean == null) { //刚刚流程过来,此处确为null
// 检查beanName和alias是否唯一
checkNameUniqueness(beanName, aliases, ele);
}
// 解析<bean>标签的相关属性,并将其添加进beanDefinition返回(主要处理属性如class,parent等相关属性,详细可阅读源码)
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//自动生成BeanName,BeanDefinitionReader
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.
//取得bean的完整类名
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
//如果类名不为null,并且自动生成的BeanName长度比类名长,并且类名在工厂中还没有作为BeanName使用,那么别名集合中添加bean的类名
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);
//返回封装的holder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}

return null;
}


至此一个Holder返回完成,其内部持有BeanDefinition对象,beanName以及BeanName映射的别名数组。我们拿到Bean的定义之后,要干大事,即将bean注册到Bean工厂中,这一部是如何做到的呢?

Bean的注册

回到
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
在我们利用委托完成节点解析并返回Holder后,调用
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
方法完成bean的注册,其代码如下,因为之前已经分析过BeanDefinitionHolder的初始化过程,此处还算较好理解

/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

// Register bean definition under primary name.
// 在holder中获取Bean的名称
String beanName = definitionHolder.getBeanName();
// 注册BeanDefinition到bean工厂(这里看到是一个BeanDefinitionRegistry对象,其实最终是一个DefaultListableBeanFactory对象)
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 注册Bean的别名
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}


BeanDefinitionRegistry和DefaultListableBeanFactory关系如下,其实最终还是注册到BeanFactory中了:



总结

总结来说,ApplicationContext将解析配置文件的工作交给BeanDefinitionReader,然后BeanDefinitionReader将配置文件读取为xml的Document文档之后,又委托给BeanDefinitionDocumentReader ,BeanDefinitionDocumentReader将具体节点的解析工作交给BeanDefinitionParserDelegate 类来完成,并将Bean的element定义转成BeanDefinition对象,然后交给BeanDefinitionHolder持有

总体流程:

1.创建BeanDefinitionReader并传入beanFactory作为注册器,读取配置文件,将配置文件转Document,并将自己放入ReaderContext中作为BeanDefinitionDocumentReader 的上下文

2.使用BeanDefinitionDocumentReader 读取Document全文,根据标签名处理不同的标签,并将bean标签交由BeanDefinitionParserDelegate 处理

3.BeanDefinitionParserDelegate 处理bean标签,读取各属性并处理别名等情况,将其封装为BeanDefinition对象,将处理结果交给BeanDefinitionHolder持有

4.在Holder对象中取得beanName和alias[],还有BeanDefinition对象,从BeanDefinitionDocumentReader 对象的上下文中取出BeanFactory对象,并将对象注册给beanfactory中

以上调用链,只处理spring-beans3.0.xsd的标签,其他自定义标签spring提供了其他方式处理

*工厂对bean的维护,另开疆拓土,此篇完事,全篇流水账。只做学习笔记之用。

最后贴个别人的图

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: