Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
2017-04-13 21:50
417 查看
前文提到最关键的地方是解析bean xml配置文件,废话不多说,直接上代码清单
//正如官方注释所说,解析import标签、alias标签、bean标签和自定义的标签 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //检查<beans>根标签的命名空间是否为空或者是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; //每个子节点都有命名空间 if (delegate.isDefaultNamespace(ele)) //解析import标签、alias标签、bean标签、内置<beans>标签 parseDefaultElement(ele, delegate); } else { //解析自定义标签,例如<context:component-scan basePackage="" /> delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
那我们根据两个部分来解读对bean xml文件的解析,分别为
DefaultBeanDefinitionDocumentReader#parseDefaultElement和
BeanDefinitionParserDelegate#parseCustomElement
DefaultBeanDefinitionDocumentReader#parseDefaultElement
解析import标签、alias标签、bean标签,代码清单如下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); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse 递归 doRegisterBeanDefinitions(ele); } }
三种解析具体方法
DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource
import标签解析代码比较长,逻辑倒是很简单,代码清单如下
```java
protected void importBeanDefinitionResource(Element ele) {
//获取标签的resource属性值
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
//resource不允许为空
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// Resolve system properties: e.g. "${user.dir}" //这里强调下,对于<import>标签,其会从System.getProperties()和System.getenv()中属性获取,优先System.getProperties() //即优先系统变量,环境变量次之 location = environment.resolveRequiredPlaceholders(location); Set<Resource> actualResources = new LinkedHashSet<Resource>(4); // Discover whether the location is an absolute or relative URI //resource属性支持URL模式,找到相应的资源并进行加载 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? if (absoluteLocation) { try { 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()]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
```
DefaultBeanDefinitionDocumentReader#processAliasRegistration
主要目的就是缓存全局别名
java protected void processAliasRegistration(Element ele) { //这里确保<alias>标签的name、alias值不为空 e.g. <alias name="key" alias="value"> 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 { //全局设置,查阅后其会保存到DefaultListableFactory的父级类`SimpleAliasRegistry#aliasMap`中 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)); } }
DefaultBeanDefinitionDocumentReader#processBeanDefinition
<bean>标签的解析处理
java protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //初始化bean标签的相应内容 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { //对bean标签中的属性和子标签,进行相应的具体解析,例如property、constructor-args 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)); } }
BeanDefinitionParserDelegate#parseBeanDefinitionElement
解析bean标签,也许会返回null在出现解析错误的时候,代码清单如下
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //获取<bean>的 id属性 String id = ele.getAttribute(ID_ATTRIBUTE); //如果有name属性,则获取name,并且可有多个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)); } String beanName = id; //当id属性不存在且name属性有值时,默认使用第一个name值 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"); } } //确保beanName的唯一性,即在应用使用前不允许有两个的beanName一致 if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } //解析除id、name属性的情况 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { //省略部分代码 ... ... String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
限于篇幅太长,其余的情况我们就不讲解了,有兴趣的可以自行前往,但在此根据读到的源码对
bean解析进行总结
parent属性代表父类,这里表示的是父类的唯一id名
不允许有
singleton属性;可有
scope属性;
可有
abstract属性,值可为true/false;
可有
lazyinit属性,值为default则沿用
beans标签中的
lazyinit,否则为true/false;
可有
autowire属性,值为default则沿用
beans标签的
autowire。
byName表示按照名称注入、
byType按照类型注入、
constructor表明按照构造函数注入、
autodetect表明自动检测(官方不建议使用)、默认则不使用注入;
可有
dependency-check属性,值有
all、
objects、
simple,默认为不检测;
可有
depends-on属性,可有多值,以,;为分隔符;
可有
autowire-candidate属性,值可为
true/false/default/空字符串;
可有
primary属性,值为true/false;
可有
init-method属性,初始化方法,对应可有
destroy-method属性;
可有
facroty-method属性,工厂方法;
可有
factory-bean属性
bean标签下可有
description标签,表示对此
bean的描述;
也可有
meta标签;
可有
lookup-method标签;
可有
replaced-method标签;
可有
constructor-arg标签;
可有
property标签,其中
value和
ref属性只可选用一个;
可有
qualifier标签,其下可有
attribute标签,同
@Qualifier注解
所有的bean信息都由
BeanDefinitionHolder对象来保存,其中的
BeanDefinition包含了一个
bean标签的所有可能内容
BeanDefinitionParserDelegate#parseCustomElement
解析自定义的标签节点,例如<context:component-scan>、
<tx:annotation-driven>等,简单看下代码清单
//containingBd此处为null public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //获取命名空间 String namespaceUri = getNamespaceURI(ele); //从map集合中获取NamespaceHandler接口 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } //调用统一的解析接口 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
按照上述的注释,我们需要查看下
namespaceURI对应的
NamespaceHandler到底是如何获取的,真实调用的是
DefaultNamespaceHandlerResolver类,稍微分析下此类
构造函数
```java
public DefaultNamespaceHandlerResolver() {
//第二个参数值为 META-INF/spring.handlers
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
//此处所用的是handlerMappingLocation,指的是handler接口类加载的文件资源
public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}
```
resolve解析方法
java public NamespaceHandler resolve(String namespaceUri) { //调用的是PropertiesLoaderUtils.loadAllProperties读取所有classpath下的'META-INF/spring.handlers'文件,保存至Map集合 Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); //声明的handler必须是NamespaceHandler的实现类 if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } //实例化Handler类 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); //初始化 namespaceHandler.init(); //缓存下 handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", ex); } catch (LinkageError err) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", err); } } }
限于篇幅太长,关于bean解析以及custom的具体解析未来会分条详细介绍,敬请期待
下节预告
Spring源码情操陶冶-AbstractApplicationContext#prepareBeanFactory
相关文章推荐
- 解析Spring源码(7)--DefaultBeanDefinitionDocumentReader
- 小读spring ioc源码(五)——BeanDefinitionDocumentReader
- Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器
- Spring源码学习--Spring配置文件解析BeanDefinitionDocumentReader(三)
- Spring源码情操陶冶-AbstractApplicationContext#registerBeanPostProcessors
- 解析Spring源码(4)--XmlBeanDefinitionReader
- Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization
- spring源码之XmlBeanDefinitionReader与bean的注册
- Spring源码入门——XmlBeanDefinitionReader解析
- spring 源码解读与设计详解:5 XmlBeanDefinitionReader与Resource
- Spring源码情操陶冶-AbstractApplicationContext#registerListeners
- 解析Spring源码(9)--XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
- Spring源码情操陶冶-ContextLoaderListener
- Spring源码情操陶冶-自定义节点的解析
- Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器
- Spring源码情操陶冶-AbstractApplicationContext#onRefresh
- Spring源码情操陶冶-AbstractApplicationContext#obtainFreshBeanFactory
- Spring源码情操陶冶-AbstractApplicationContext#prepareRefresh
- 小读spring ioc源码(四)——BeanDefinitionReader
- Spring源码情操陶冶-AbstractApplicationContext#invokeBeanFactoryPostProcessors