在上一篇文章中,分析了ApplicationContext容器的创建,加载资源文件,将资源文件读取为Document。spring将xml文件中的Bean注册spring定义的BeanDefinition对象。在DefaultBeanDefinitionDocumentReader中对Document属性的解析委托给BeanDefinitionParserDelegate这个代理类来实现的。
Bean注册前的准备
DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法实现如下,首先获取Document的根元素,接着调用doRegisterBeanDefinitions(root)进行注册
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
//获取根元素
Element root = doc.getDocumentElement();
//注册BeanDefinition
doRegisterBeanDefinitions(root);
}
doRegisterBeanDefinitions(root)方法的实现如下:
protected void doRegisterBeanDefinitions(Element root) {
//获取代理类
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//是否为默认命名空间
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
//是否有profile属性
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
preProcessXml(root);
//解析BeanDefinition
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
根据不同节点名进行解析
parseBeanDefinitions(root, this.delegate)方法是解析根源素下定义的每一个bean。首先,获取节点List。其次,判断每个元素是否为默认的命名空间中的元素,然后交给不同的方法去解析,具体的实现如下:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//如果根元素为默认命名空间中的元素
if (delegate.isDefaultNamespace(root)) {
//获取字元素List
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);
}
}
spring默认命名空间节点的解析
下面,我们首先看spring默认命名空间元素的解析过程,parseDefaultElement(ele, delegate)方法的实现如下:
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节点的解析
import元素是引入其他的配置文件,resource属性是配置文件的路径,importBeanDefinitionResource(ele)方法的实现如下,省略了异常处理代码:
protected void importBeanDefinitionResource(Element ele) {
//获取resource属性值,即其他配置文件的路径
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 解析路径,如"${user.dir}" 这样的路径是从在propertie文件中加载的
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// 判断location 是绝对路径还是相对路径
boolean absoluteLocation = false;
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
// 绝对路径
if (absoluteLocation) {
//调用loadBeanDefinitions(location, actualResources)方法解析此配置文件
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
}
}
//相对路径
else {
int importCount;
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
//调用loadBeanDefinitions(relativeResource)方法解析此配置文件
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
}
}
//广播Import元素处理事件
Resource[] actResArray = actualResources.toArray(new Resource[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
- alias节点的解析
下面介绍alias元素的方法,processAliasRegistration(ele)方法的实现如下:
protected void processAliasRegistration(Element ele) {
//获取name属性
String name = ele.getAttribute(NAME_ATTRIBUTE);
//获取alias属性
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) {
//调用SimpleAliasRegistry类的registerAlias(name, alias)进行注册
getReaderContext().getRegistry().registerAlias(name, alias);
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
在SimpleAliasRegistry中定义了aliasMap来存储alias和name的关系,具体实现如下:
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
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) {
//如果alias和name相等,将此关系移除
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
//先从aliasMap获取key为alias的beanName
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
//如果已存在,return
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
//如果不存在,判断alias是否可以继承,默认是true
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
//检查是否存在循环依赖
checkForAliasCircle(name, alias);
//注册alias和name
this.aliasMap.put(alias, name);
}
}
}
- bean节点的解析
processBeanDefinition(ele, delegate)实现如下:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析bean元素,创建BeanDefinitionHolder实例
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//完成必须的装配
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 进行最终的注册bean
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));
}
}
bean元素的解析和注册相对复杂,在下一节中讨论。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。