文档章节

基于XmlBeanFactory加载bean分析:parseDefaultElement(一)

qwweeezhengwei
 qwweeezhengwei
发布于 2017/08/01 18:12
字数 1130
阅读 3
收藏 0
DefaultBeanDefinitionDocumentReader.java
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        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;
                    if(delegate.isDefaultNamespace(ele)) {
                        //加载默认的阶段配置信息【程序入口】
                        this.parseDefaultElement(ele, delegate);
                    } else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }

    }

parseDefaultElement(ele, delegate)该方法会根据当前节点类型做三件事情

1、导入import

例如:

<import resource="applicationContext-mybean.xml"></import>

2、alias别名

<bean name="myBean,wahaha,gagBean" class="com.wolf.bean.Mybean1">
    <property name="name">
        <value>郑先生</value>
    </property>
    <property name="pwd">
        <value>666666888888</value>
    </property>
</bean>

<!--给myBean起别名-->
<alias name="myBean" alias="gagBean,gagaBean,gagagaBean"></alias>

3、bean定义

<bean name="myBean2" class="com.wolf.bean.Mybean1">
    <property name="name">
        <value>郑先生2</value>
    </property>
    <property name="pwd">
        <value>2222222</value>
    </property>
</bean>

4、beans集合

对于beans集合系统会重新走doRegisterBeanDefinitions处理流程,跟程序来源一样。

 

parseDefaultElement(ele, delegate)代码实现如下

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if(delegate.nodeNameEquals(ele, "import")) {//导入
		this.importBeanDefinitionResource(ele);
	} else if(delegate.nodeNameEquals(ele, "alias")) {//别名
		this.processAliasRegistration(ele);
	} else if(delegate.nodeNameEquals(ele, "bean")) {//bean
		this.processBeanDefinition(ele, delegate);
	} else if(delegate.nodeNameEquals(ele, "beans")) {//bean集合
		this.doRegisterBeanDefinitions(ele);
	}

}

一、import

protected void importBeanDefinitionResource(Element ele) {
	String location = ele.getAttribute("resource");
	if(!StringUtils.hasText(location)) {
		this.getReaderContext().error("Resource location must not be empty", ele);
	} else {
		location = this.getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
		LinkedHashSet actualResources = new LinkedHashSet(4);
		boolean absoluteLocation = false;

		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		} catch (URISyntaxException var11) {
			;
		}

		int actResArray;
		if(absoluteLocation) {
			try {
				actResArray = this.getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
			} catch (BeanDefinitionStoreException var10) {
				this.getReaderContext().error("Failed to import bean definitions from URL location [" + location + "]", ele, var10);
			}
		} else {
			try {
				Resource relativeResource = this.getReaderContext().getResource().createRelative(location);
				//判断当前资源文件是否存在
				if(relativeResource.exists()) {
					//执行XmlBeanDefinitionReader的loadBeanDefinitions,跟加载资源的流程一致
					//所以import资源处理操作是非常简单的
					actResArray = this.getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				} else {
					String baseLocation = this.getReaderContext().getResource().getURL().toString();
					actResArray = this.getReaderContext().getReader().loadBeanDefinitions(StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}

			} catch (IOException var8) {
				this.getReaderContext().error("Failed to resolve current resource location", ele, var8);
			} catch (BeanDefinitionStoreException var9) {
				this.getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, var9);
			}
		}
		
		
		Resource[] actResArray1 = (Resource[])actualResources.toArray(new Resource[actualResources.size()]);
		//这里做了一个事件通知
		this.getReaderContext().fireImportProcessed(location, actResArray1, this.extractSource(ele));
	}
}

二、alias别名解析

protected void processAliasRegistration(Element ele) {
	//通过当前节点的别名信息配置
	//name 表示BeanName
	String name = ele.getAttribute("name");
	//别名信息
	String alias = ele.getAttribute("alias");
	boolean valid = true;
	if(!StringUtils.hasText(name)) {
		this.getReaderContext().error("Name must not be empty", ele);
		valid = false;
	}

	if(!StringUtils.hasText(alias)) {
		this.getReaderContext().error("Alias must not be empty", ele);
		valid = false;
	}

	if(valid) {
		try {
			//XmlReaderContext-->BeanDefinitionRegistry-->GenericApplicationContext(接口BeanDefinitionRegistry)的registerAlias方法[DefaultListableBeanFactory beanFactory]
			//其实就是往DefaultListableBeanFactory beanFactory中设置别名信息(SimpleAliasRegistry为具体别名注册实现)
			//private final Map<String, String> aliasMap = new ConcurrentHashMap(16); 别名属性记录器
			this.getReaderContext().getRegistry().registerAlias(name, alias);
		} catch (Exception var6) {
			this.getReaderContext().error("Failed to register alias \'" + alias + "\' for bean with name \'" + name + "\'", ele, var6);
		}

		this.getReaderContext().fireAliasRegistered(name, alias, this.extractSource(ele));
	}

}

 

根据类图结构可以看到

DefaultListableBeanFactory为SimpleAliasRegistry的子类

然后在父类实现了别名的注册,子类直接使用即可。

--分部代码实现
DefaultBeanDefinitionDocumentReader.java类中持有XmlReaderContext readerContext对象
其中this.getReaderContext()其实就是去获取XmlReaderContext
protected final XmlReaderContext getReaderContext() {
	return this.readerContext;
}
当拿到XmlReaderContext对象后,通过XmlReaderContext提供的getRegistry()获取注册器
XmlReaderContext.java类中持有XmlBeanDefinitionReader reader xmlBean读取器,在xmlBean读取器中获取注册器
public final BeanDefinitionRegistry getRegistry() {
	return this.reader.getRegistry();
}
AbstractBeanDefinitionReader.java类中持有
private final BeanDefinitionRegistry registry//BeanDefinitionRegistry为一个接口
public final BeanDefinitionRegistry getRegistry() {
	return this.registry;
}
GenericApplicationContext.java持有DefaultListableBeanFactory beanFactory
public void registerAlias(String beanName, String alias) {
	this.beanFactory.registerAlias(beanName, alias);
}
SimpleAliasRegistry.java
private final Map<String, String> aliasMap = new ConcurrentHashMap(16);
public void registerAlias(String name, String alias) {
	if(alias.equals(name)) {
		this.aliasMap.remove(alias);
	} else {
		String registeredName = (String)this.aliasMap.get(alias);
		if(registeredName != null) {
			if(registeredName.equals(name)) {
				return;
			}

			if(!this.allowAliasOverriding()) {
				throw new IllegalStateException("Cannot register alias \'" + alias + "\' for name \'" + name + "\': It is already registered for name \'" + registeredName + "\'.");
			}
		}

		this.checkForAliasCircle(name, alias);
		this.aliasMap.put(alias, name);
	}

}

总结:其实别名的注册思路很简单。就是在beanFactory容器中去查找当前beanName是否存在别名,如果存在则直接return;如果别名与beanName的名称相同时也return;

这里还加入了一个逻辑就是判断别名是否能够被替换在目前的代码实现直接返回了true,也就是说默认情况可以直接替换别名的。

protected boolean allowAliasOverriding() {
    return true;
}
this.checkForAliasCircle(name, alias)

这里做了一个验证,就是验证当前注册的别名是否被其他的beanName注册过,如果注册过会抛出

throw new IllegalStateException("Cannot register alias \'" + alias + "\' for name \'" + name + "\': Circular reference - \'" + name + "\' is a direct or indirect alias for \'" + alias + "\' already")异常

三、beans处理

beans处理逻辑也比较简单

直接调用了this.doRegisterBeanDefinitions(ele)逻辑,然后我们跟踪代码,发现跟处理rootElement逻辑相同。

protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
    if(this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute("profile");
        if(StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
            if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if(this.logger.isInfoEnabled()) {
                    this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                }

                return;
            }
        }
    }

    this.preProcessXml(root);
    this.parseBeanDefinitions(root, this.delegate);
    this.postProcessXml(root);
    this.delegate = parent;
}

四、bean标签处理

TIPS:针对bean标签的处理逻辑相对比较复杂,这里要深入分析。

【接】基于XmlBeanFactory加载bean分析:parseDefaultElement(二)

© 著作权归作者所有

qwweeezhengwei
粉丝 0
博文 13
码字总数 31124
作品 0
成都
私信 提问
【Spring】BeanFactory解析bean详解

在该文中来讲讲Spring框架中BeanFactory解析bean的过程,该文之前在小编原文中有发表过,要看原文的可以直接点击原文查看,先来看一个在Spring中一个基本的bean定义与使用。 Spring配置文件r...

weknow
2017/04/05
0
0
【Spring】BeanFactory解析bean详解

本文是Spring源码分析中的一篇,来讲讲Spring框架中BeanFactory解析bean的过程,先来看一个在Spring中一个基本的bean定义与使用。(也可以点击公号查看) Spring配置文件root.xml定义如下: ...

weknow
2018/09/09
84
0
spring源码-bean之初始化-1

  一、spring的IOC控制反转:控制反转——Spring通过一种称作控制反转(IOC)的技术促进了松耦合。当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建...

小不点丶
2018/08/09
0
0
XmlBeanFactory的初始化

XmlBeanFactory继承DefaultListableBeanFactory,关系图如下 内部通过XmlBeanDefinitionReader来从xml中读取bean的定义,即委托给XmlBeanDefinitionReader,XmlBeanDefinitionReader是读取x...

五年级小学生
07/01
5
0
【Spring】- IOC容器初始化过程

IOC容器案例:XmlBeanFactory XmlBeanFactory容器初始化过程: 步骤1:XmlBeanFactory 使用 ClassPathResource 映射Spring XML配置文件~ new ClassPathResource("com/zhiwei/ioc/application......

ZeroneLove
03/02
7
0

没有更多内容

加载失败,请刷新页面

加载更多

006-Sigle-基于blockstack去中心化博客

本篇文章主要讲解有关基于Blockstack的Sigle是一个去中心化的博客项目; 官网地址:https://www.sigle.io/ Github地址:https://github.com/pradel/sigle 页面展示: 介绍: A beautiful de...

Riverzhou
28分钟前
10
0
驰骋工作流引擎开发平台属性功能的隐藏显示介绍

关键字: 工作流程管理系统 工作流引擎 asp.net工作流引擎 java工作流引擎. 表单引擎 工作流功能说明 工作流设计 工作流快速开发平台 业务流程管理 bpm工作流系统 java工作流主流框架 自定义...

孟娟
30分钟前
10
0
MyBatis binding 模块分析

MyBatis binding 模块分析 binding功能代码所在包 org.apache.ibatis.binding binding模块作用 封装ibatis编程模型 ibatis编程模型中,SqlSession作为sql执行的入口,实用方法为sqlSession.se...

红妍落日
32分钟前
9
0
网易互娱的数据库选型和 TiDB 应用实践

作者介绍:李文杰,网易互娱计费组,高级数据库管理工程师,TiDB User Group Ambassador。 一、业务架构简介 计费组是为网易互娱产品提供统一登录和支付高效解决方案的公共支持部门,对内是互...

TiDB
39分钟前
10
0
Debezium接入Mysql遇到到的Tinyint坑

问题背景: 在Debezium做数据初始化的时候,对于一些tinyint字段的值,出现0,1的值的异常。 经过源码排查,数据在JDBC上面,读取到的数据是Boolean值。 通过排查,原来是MYSQL特有的数据问题...

吐槽的达达仔
47分钟前
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部