本文通过阅读源码,分析spring如何读取xml配置中的bean元数据。
关于阅读源码的思路,可参考 -- 如何阅读java源码
本文主要分析XmlBeanFactory,XmlBeanFactory是一个简单的容器, 从XML文件中读取配置元数据。
BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("spring.xml"));
XmlBeanFactory已过时,但好在简单,便于分析源码。
XmlBeanFactory#构造函数 -> XmlBeanDefinitionReader#loadBeanDefinitions
-> XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); // #1 return registerBeanDefinitions(doc, resource); // #2 } ... }
#1
加载xml到Document类(使用org.w3c.dom读取xml文件)
#2
registerBeanDefinitions -> 解析xml数据
XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // #1 int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // #2 return getRegistry().getBeanDefinitionCount() - countBefore; }
#1
构建BeanDefinitionDocumentReader
#2
createReaderContext -> 构建ReaderContext,ReaderContext中存放了xml的resource,XmlBeanDefinitionReader等信息,这些后面都要使用。
registerBeanDefinitions -> 使用BeanDefinitionDocumentReader解析xml数据
注意,ReaderContext中存放了XmlBeanDefinitionReader,而XmlBeanDefinitionReader中存放了bean注册器BeanDefinitionRegistry(后面注册BeanDefinition要使用)。这个注册器就是XmlBeanFactory,XmlBeanFactory中构建XmlBeanDefinitionReader时将this作为Registry参数
XmlBeanDefinitionReader#reader
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
XmlBeanFactory的父类DefaultListableBeanFactory实现了BeanDefinitionRegistry。
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions -> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // #1 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); // #2 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); // #3 parseBeanDefinitions(root, this.delegate); // #4 postProcessXml(root); this.delegate = parent; }
#1
判断元素的命名空间是否为spring的beans空间,这个方法后面也有使用
#2
处理PROFILE元素属性
#3
预处理,为子类提供的扩展方法
#4
parseBeanDefinitions -> 解析beans元素
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
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)) { parseDefaultElement(ele, delegate); // #1 } else { delegate.parseCustomElement(ele); // #2 } } } } else { delegate.parseCustomElement(root); } }
#1
对于beans命名空间的元素,使用默认的方法进行解析
#2
处理非beans命名空间的元素,使用对应的解析器处理
用户也可以自定义标签及标签解析器。
DefaultBeanDefinitionDocumentReader#parseDefaultElement
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // #1 importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // #2 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // #3 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // #4 doRegisterBeanDefinitions(ele); } }
#1
解析import元素
#2
解析alias元素
#3
解析bean元素
#4
解析beans元素
主要看对bean元素的解析,DefaultBeanDefinitionDocumentReader#processBeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); // #1 if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); // #2 try { BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); // #3 } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); // #4 } }
#1
根据xmleizo,生成BeanDefinitionHolder
#2
应用NamespaceHandler#decorate扩展方法
#3
将BeanDefinition注册到spring上下文
#4
发送注册事件
这里出现了一个很重要的类BeanDefinition。
BeanDefinition是spring对bean元数据的抽象, 是配置文件中 <bean>
元素在spring容器中的内部表示类,存储了bean的元数据, 如属性PropertyValues, BeanClassName,Scope等。
BeanDefinitionHolder中持有BeanDefinition实例,以及beanName和aliases属性。
BeanDefinitionParserDelegat#parseBeanDefinitionElement有几个重载方法,最终都调用如下方法
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); // #1 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); // #2 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); // #3 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // #4 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // #5 parseConstructorArgElements(ele, bd); // #6 parsePropertyElements(ele, bd); // #7 parseQualifierElements(ele, bd); // #8 bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } ... }
#1
使用className和parent创建BeanDefinition
#2
处理scope等元素属性
#3
处理meta元素属性
#4
处理lookup-method元素属性
#5
处理replaced-method元素属性
#6
处理constructor-arg构造方法参数
#7
处理property元素
#8
处理qualifier元素
看一下如何处理property元素
BeanDefinitionParserDelegate#parsePropertyElements -> BeanDefinitionParserDelegate#parsePropertyElement
public void parsePropertyElement(Element ele, BeanDefinition bd) { String propertyName = ele.getAttribute(NAME_ATTRIBUTE); // #1 if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } Object val = parsePropertyValue(ele, bd, propertyName); // #2 PropertyValue pv = new PropertyValue(propertyName, val); // #3 parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); // #4 } finally { this.parseState.pop(); } }
#1
解析属性名
#2
将xml配置的属性值转化为内部表示类
#3
创建PropertyValue,PropertyValue中name存放属性名,value存放了xml配置原始值。
#4
添加到BeanDefinition,BeanDefinition使用MutablePropertyValues存放所有的属性信息。
xml配置原始值并不是属性最终值,而是xml配置在spring中对应的内部表示类,如property元素的value属性会表示为TypedStringValue(类似于BeanDefinition表示<bean>元素)。
BeanDefinitionParserDelegate#parsePropertyValue
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { ... boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } if (hasRefAttribute) { // #1 String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } else if (hasValueAttribute) { // #2 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } else if (subElement != null) { // #3 return parsePropertySubElement(subElement, bd); } else { // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); return null; } }
#1
如果property元素存在ref属性,解析为RuntimeBeanReference
#2
如果存在value属性,解析为TypedStringValue
#3
如果存在子标签,就解析子标签。子标签和spring内部表示类对应如下
子标签为bean,解析为BeanDefinitionHolder
子标签为ref,解析为RuntimeBeanReference
子标签为idref,解析为RuntimeBeanNameReference
子标签为value,解析为TypedStringValue
子标签为null,解析为TypedStringValue
子标签为array,解析为ManagedArray
子标签为list,解析为ManagedList
子标签为set,解析为ManagedSet
子标签为map,解析为ManagedMap
子标签为props,解析为ManagedProperties
回到DefaultBeanDefinitionDocumentReader#processBeanDefinition方法 #3
步骤, 看一下如何将BeanDefinition注册到spring上下文
BeanDefinitionReaderUtils#registerBeanDefinition
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // #1 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); // #2 } } }
#1
使用beanName注册bean
#2
注册别名alias
注册器registry从ReaderContext中获取,实际就是DefaultListableBeanFactory(注意上文对ReaderContext的说明)。
DefaultListableBeanFactory#registerBeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { ... BeanDefinition oldBeanDefinition; oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { ... } else { if (hasBeanCreationStarted()) { // #1 synchronized (this.beanDefinitionMap) { // #2 this.beanDefinitionMap.put(beanName, beanDefinition); // #3 List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; // #4 if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; // #5 } } } else { // #6 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
#1
判断spring是否进入构建bean的阶段
#2
beanDefinitionNames/manualSingletonNames不是线程安全类,需要加锁同步
#3
添加beanDefinition到DefaultListableBeanFactory#beanDefinitionMap
#4
通过写时复制,添加beanName到DefaultListableBeanFactory#beanDefinitionNames
#5
通过写时复制,删除manualSingletonNames的DefaultListableBeanFactory#beanDefinitionNames
#6
还在注册阶段,不需要加锁同步
最后, 看一下DefaultBeanDefinitionDocumentReader#parseBeanDefinitions方法 #2
步骤对非beans空间的xml标签的处理, BeanDefinitionParserDelegate#parseCustomElement
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); // #1 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)); // #2 }
#1
通过命名空间获取特定的NamespaceHandler
#2
使用NamespaceHandler进行处理
如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!