Spring源码--IOC容器实现(3)--BeanDefinition的载入和解析

前言

Github:https://github.com/yihonglei/thinking-in-spring

BeanDefinition的载入和解析

上文分析了BeanDefiniton的Resource定位过程:

Spring源码--IOC容器实现(2)--BeanDefinition的Resource定位

这篇文章分析下BeanDefinition信息的载入过程。

载入过程就是把Resource转化为BeanDefinition作为一个Spring IOC容器内部表示的数据结构的过程。

IOC容器对Bean的管理和依赖注入功能的实现,就是通过对其持有的BeanDefinition进行各种相关操作来完成的。

这些BeanDefinition数据在IOC容器中通过一个HashMap来保护和维护。在上文中,我们从refresh()入口开始,

一直追溯到AbstractBeanDefinitionReader.loadBeanDefinitions(),找到BeanDefinition需要的Resource资源文件。

AbstractBeanDefinitionReader.loadBeanDefinitions()方法源码:


 
 
  1. public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
  2. // 这里得到当前定义的ResourceLoader,默认的使用DefaultResourceLoader
  3. ResourceLoader resourceLoader = getResourceLoader();
  4. if (resourceLoader == null) {
  5. throw new BeanDefinitionStoreException(
  6. "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
  7. }
  8. /**
  9. * 这里对Resource的路径模式进行解析,得到需要的Resource集合,
  10. * 这些Resource集合指向了我们定义好的BeanDefinition的信息,可以是多个文件。
  11. */
  12. if (resourceLoader instanceof ResourcePatternResolver) {
  13. // Resource pattern matching available.
  14. try {
  15. // 调用DefaultResourceLoader的getResources完成具体的Resource定位
  16. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
  17. int loadCount = loadBeanDefinitions(resources);
  18. if (actualResources != null) {
  19. for (Resource resource : resources) {
  20. actualResources.add(resource);
  21. }
  22. }
  23. if (logger.isDebugEnabled()) {
  24. logger.debug( "Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
  25. }
  26. return loadCount;
  27. }
  28. catch (IOException ex) {
  29. throw new BeanDefinitionStoreException(
  30. "Could not resolve bean definition resource pattern [" + location + "]", ex);
  31. }
  32. }
  33. else {
  34. // Can only load single resources by absolute URL.
  35. // 通过ResourceLoader来完成位置定位
  36. Resource resource = resourceLoader.getResource(location);
  37. int loadCount = loadBeanDefinitions(resource);
  38. if (actualResources != null) {
  39. actualResources.add(resource);
  40. }
  41. if (logger.isDebugEnabled()) {
  42. logger.debug( "Loaded " + loadCount + " bean definitions from location [" + location + "]");
  43. }
  44. return loadCount;
  45. }
  46. }

资源文件Resource有了,看下是如何将Resource文件载入到BeanDefinition中的?

1、将xml文件转换成Document对象

Resource载入的具体实现在AbstractBeanDefinitionReader.loadBeanDefinitions(),源码:


 
 
  1. @Override
  2. public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
  3. /**
  4. * 如果Resource为空,则停止BeanDefinition载入,然后启动载入BeanDefinition的过程,
  5. * 这个过程会遍历整个Resource集合所包含的BeanDefinition信息。
  6. */
  7. Assert.notNull(resources, "Resource array must not be null");
  8. int counter = 0;
  9. for (Resource resource : resources) {
  10. counter += loadBeanDefinitions(resource);
  11. }
  12. return counter;
  13. }

这里调用loadBeanDefinitions(Resource... resources)方法,但是这个方法AbstractBeanDefinitionReader中

没有具体实现。具体实现在BeanDefinitionReader的子类XmlBeanDefinitionReader中实现。

XmlBeanDefinitionReader.loadBeanDefinitions()源码:

loadBeanDefinitions--》调用的入口


 
 
  1. @Override
  2. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  3. return loadBeanDefinitions( new EncodedResource(resource));
  4. }

loadBeanDefinitions--》载入XML形式的BeanDefinition的地方:


 
 
  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  2. Assert.notNull(encodedResource, "EncodedResource must not be null");
  3. if (logger.isInfoEnabled()) {
  4. logger.info( "Loading XML bean definitions from " + encodedResource.getResource());
  5. }
  6. // 这里是获取线程局部变量
  7. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  8. if (currentResources == null) {
  9. currentResources = new HashSet<EncodedResource>( 4);
  10. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  11. }
  12. if (!currentResources.add(encodedResource)) {
  13. throw new BeanDefinitionStoreException(
  14. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  15. }
  16. try {
  17. // 这里得到XML文件,新建IO文件输入流,准备从文件中读取内容
  18. InputStream inputStream = encodedResource.getResource().getInputStream();
  19. try {
  20. InputSource inputSource = new InputSource(inputStream);
  21. if (encodedResource.getEncoding() != null) {
  22. inputSource.setEncoding(encodedResource.getEncoding());
  23. }
  24. // 具体读取过程的方法
  25. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  26. }
  27. finally {
  28. inputStream.close();
  29. }
  30. }
  31. catch (IOException ex) {
  32. throw new BeanDefinitionStoreException(
  33. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  34. }
  35. finally {
  36. currentResources.remove(encodedResource);
  37. if (currentResources.isEmpty()) {
  38. this.resourcesCurrentlyBeingLoaded.remove();
  39. }
  40. }
  41. }

doLoadBeanDefinitions--》具体读取过程地方:


 
 
  1. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  2. throws BeanDefinitionStoreException {
  3. try {
  4. // 将XML文件转换为DOM对象,解析过程由documentLoader实现
  5. Document doc = doLoadDocument(inputSource, resource);
  6. // 这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
  7. return registerBeanDefinitions(doc, resource);
  8. }
  9. catch (BeanDefinitionStoreException ex) {
  10. throw ex;
  11. }
  12. catch (SAXParseException ex) {
  13. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  14. "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
  15. }
  16. catch (SAXException ex) {
  17. throw new XmlBeanDefinitionStoreException(resource.getDescription(),
  18. "XML document from " + resource + " is invalid", ex);
  19. }
  20. catch (ParserConfigurationException ex) {
  21. throw new BeanDefinitionStoreException(resource.getDescription(),
  22. "Parser configuration exception parsing XML from " + resource, ex);
  23. }
  24. catch (IOException ex) {
  25. throw new BeanDefinitionStoreException(resource.getDescription(),
  26. "IOException parsing XML document from " + resource, ex);
  27. }
  28. catch (Throwable ex) {
  29. throw new BeanDefinitionStoreException(resource.getDescription(),
  30. "Unexpected exception parsing XML document from " + resource, ex);
  31. }
  32. }

到这里完成了XML转化为Document对象,主要经历两个过程:资源文件转化为IO和XML转换为Document对象。

2、按照Spring的Bean规则对Document对象进行解析

在上面分析中,通过调用XML解析器将Bean定义资源文件转换得到Document对象,

但是这些Document对象并没有按照Spring的Bean规则进行解析。

需要按照Spring的Bean规则对Document对象进行解析。

如何对Document文件进行解析?

XmlBeanDefinitionReader.registerBeanDefinitions()源码:


 
 
  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. // 这里得到BeanDefinitionDocumentReader来对XML的BeanDefinition进行解析
  3. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  4. // 获得容器中注册的Bean数量
  5. int countBefore = getRegistry().getBeanDefinitionCount();
  6. // 具体的解析过程在registerBeanDefinitions中完成
  7. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  8. // 统计解析的Bean数量
  9. return getRegistry().getBeanDefinitionCount() - countBefore;
  10. }

按照Spring的Bean规则对Document对象解析的过程是在接口BeanDefinitionDocumentReader

的实现类DefaultBeanDefinitionDocumentReader中实现的。

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()源码:


 
 
  1. @Override
  2. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  3. this.readerContext = readerContext;
  4. logger.debug( "Loading bean definitions");
  5. // 获取根元素
  6. Element root = doc.getDocumentElement();
  7. // 具体载入过程
  8. doRegisterBeanDefinitions(root);
  9. }

DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()源码:


 
 
  1. protected void doRegisterBeanDefinitions(Element root) {
  2. // Any nested <beans> elements will cause recursion in this method. In
  3. // order to propagate and preserve <beans> default-* attributes correctly,
  4. // keep track of the current (parent) delegate, which may be null. Create
  5. // the new (child) delegate with a reference to the parent for fallback purposes,
  6. // then ultimately reset this.delegate back to its original (parent) reference.
  7. // this behavior emulates a stack of delegates without actually necessitating one.
  8. BeanDefinitionParserDelegate parent = this.delegate;
  9. this.delegate = createDelegate(getReaderContext(), root, parent);
  10. if ( this.delegate.isDefaultNamespace(root)) {
  11. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  12. if (StringUtils.hasText(profileSpec)) {
  13. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  14. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  15. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
  16. if (logger.isInfoEnabled()) {
  17. logger.info( "Skipped XML bean definition file due to specified profiles [" + profileSpec +
  18. "] not matching: " + getReaderContext().getResource());
  19. }
  20. return;
  21. }
  22. }
  23. }
  24. preProcessXml(root);
  25. //从Document的根元素开始进行Bean定义的Document对象
  26. parseBeanDefinitions(root, this.delegate);
  27. postProcessXml(root);
  28. this.delegate = parent;
  29. }

根据Element载入BeanDefinition。

DefaultBeanDefinitionDocumentReader.parseBeanDefinitions()源码:


 
 
  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  2. // 如果使用了Spring默认的XML命名空间
  3. if (delegate.isDefaultNamespace(root)) {
  4. // 遍历根元素的所有子节点
  5. NodeList nl = root.getChildNodes();
  6. for ( int i = 0; i < nl.getLength(); i++) {
  7. Node node = nl.item(i);
  8. // 如果该节点是XML元素节点
  9. if (node instanceof Element) {
  10. Element ele = (Element) node;
  11. // 如果该节点使用的是Spring默认的XML命名空间
  12. if (delegate.isDefaultNamespace(ele)) {
  13. // 使用Spring的Bean规则解析元素节点
  14. parseDefaultElement(ele, delegate);
  15. }
  16. else {
  17. // 没有使用Spring默认的XML命名空间,则使用用户自定义的解析规则解析元素节点
  18. delegate.parseCustomElement(ele);
  19. }
  20. }
  21. }
  22. }
  23. else {
  24. // Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的解析规则解析Document根节点
  25. delegate.parseCustomElement(root);
  26. }
  27. }

该方法作用,使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象。

 

这里主要是看节点元素是否是Spring的规范,因为它允许我们自定义解析规范,

那么正常我们是利用Spring的规则,所以我们来看parseDefaultElement(ele, delegate)方法。

DefaultBeanDefinitionDocumentReader.parseDefaultElement()源码:


 
 
  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  2. // 如果元素节点是<Import>导入元素,进行导入解析
  3. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  4. importBeanDefinitionResource(ele);
  5. }
  6. // 如果元素节点是<Alias>别名元素,进行别名解析
  7. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  8. processAliasRegistration(ele);
  9. }
  10. // 如果普通的<Bean>元素,按照Spring的Bean规则解析元素
  11. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  12. processBeanDefinition(ele, delegate);
  13. }
  14. // 如果普通的<Beans>元素,调用doRegisterBeanDefinitions()递归处理
  15. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  16. // recurse
  17. doRegisterBeanDefinitions(ele);
  18. }
  19. }

根据Bean的属性,调用不同的规则解析元素,这里讨论普通Bean元素的解析规则,

调用processBeanDefinition规则进行Bean处理。

DefaultBeanDefinitionDocumentReader.processBeanDefinition()源码:


 
 
  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2. /**
  3. * BeanDefinitionHolder是对BeanDefinition对象的封装,封装了BeanDefinition、Bean的名字和别名。
  4. * 用来完成想IOC容器注册。得到BeanDefinitionHolder就意味着是通过BeanDefinitionParserDelegate
  5. * 对XML元素的信息按照Spring的Bean规则进行解析得到的。
  6. *
  7. */
  8. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  9. if (bdHolder != null) {
  10. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  11. try {
  12. // Register the final decorated instance.
  13. // 这里是向IOC容器注册解析得到的BeanDefinition的地方
  14. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  15. }
  16. catch (BeanDefinitionStoreException ex) {
  17. getReaderContext().error( "Failed to register bean definition with name '" +
  18. bdHolder.getBeanName() + "'", ele, ex);
  19. }
  20. // Send registration event.
  21. // 在BeanDefinition向IOC容器注册以后,发送消息
  22. getReaderContext().fireComponentRegistered( new BeanComponentDefinition(bdHolder));
  23. }
  24. }

3、Bean资源的解析

具体解析过程委托给BeanDefinitionParserDelegate的parseBeanDefinitionElement来完成。

BeanDefinitionParserDelegate.parseBeanDefinitionElement()源码:

调用解析方法入口:


 
 
  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
  2. return parseBeanDefinitionElement(ele, null);
  3. }

具体对Bean元素定义的处理过程:


 
 
  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
  2. // 在这里取得<bean>元素中定义的id、name、aliase属性值
  3. String id = ele.getAttribute(ID_ATTRIBUTE);
  4. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  5. List<String> aliases = new ArrayList<String>();
  6. if (StringUtils.hasLength(nameAttr)) {
  7. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  8. aliases.addAll(Arrays.asList(nameArr));
  9. }
  10. String beanName = id;
  11. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  12. beanName = aliases.remove( 0);
  13. if (logger.isDebugEnabled()) {
  14. logger.debug( "No XML 'id' specified - using '" + beanName +
  15. "' as bean name and " + aliases + " as aliases");
  16. }
  17. }
  18. if (containingBean == null) {
  19. checkNameUniqueness(beanName, aliases, ele);
  20. }
  21. // 启动对bean元素的详细解析
  22. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  23. if (beanDefinition != null) {
  24. if (!StringUtils.hasText(beanName)) {
  25. try {
  26. if (containingBean != null) {
  27. beanName = BeanDefinitionReaderUtils.generateBeanName(
  28. beanDefinition, this.readerContext.getRegistry(), true);
  29. }
  30. else {
  31. beanName = this.readerContext.generateBeanName(beanDefinition);
  32. // Register an alias for the plain bean class name, if still possible,
  33. // if the generator returned the class name plus a suffix.
  34. // This is expected for Spring 1.2/2.0 backwards compatibility.
  35. String beanClassName = beanDefinition.getBeanClassName();
  36. if (beanClassName != null &&
  37. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  38. ! this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  39. aliases.add(beanClassName);
  40. }
  41. }
  42. if (logger.isDebugEnabled()) {
  43. logger.debug( "Neither XML 'id' nor 'name' specified - " +
  44. "using generated bean name [" + beanName + "]");
  45. }
  46. }
  47. catch (Exception ex) {
  48. error(ex.getMessage(), ele);
  49. return null;
  50. }
  51. }
  52. String[] aliasesArray = StringUtils.toStringArray(aliases);
  53. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  54. }
  55. return null;
  56. }

上面介绍了对Bean元素进行解析的过程,也即是BeanDefinition依据XML的<bean>定义被创建的过程。

这个BeanDefinition可以看成是对<bean>定义的抽象。这个数据对象中封装的数据大多都是与<bean>

定义相关的,也有很多就是我们在定义Bean时看到的那些Spring标记,比如常见的init-method、destory-method、

factory-method等等,这个BeanDefinition数据类型非常重要,它封装了很多基本数据,这些基本数据都是IOC容器

需要的。有了这些基本数据,IOC容器才能对Bean配置进行处理,才能实现相应的容器特性。

BeanDefinition定义元素的处理:


 
 
  1. public AbstractBeanDefinition parseBeanDefinitionElement(
  2. Element ele, String beanName, BeanDefinition containingBean) {
  3. this.parseState.push( new BeanEntry(beanName));
  4. /**
  5. * 这里只读取定义的<bean>中设置的class名字,然后载入到BeanDefinition中去,
  6. * 只是做个记录,并不涉及对象的实例化过程,对象的实例化实际上是在依赖注入时完成的。
  7. */
  8. String className = null;
  9. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  10. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  11. }
  12. try {
  13. String parent = null;
  14. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  15. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  16. }
  17. // 生成需要的BeanDefinition对象,为Bean定义信息的载入做准备
  18. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  19. // 对当前的Bean元素进行属性解析,并设置description的信息
  20. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  21. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  22. // 对Bean元素信息进行解析
  23. parseMetaElements(ele, bd);
  24. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  25. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  26. // 解析<bean>的构造函数设置
  27. parseConstructorArgElements(ele, bd);
  28. // 解析<bean>的property设置
  29. parsePropertyElements(ele, bd);
  30. parseQualifierElements(ele, bd);
  31. bd.setResource( this.readerContext.getResource());
  32. bd.setSource(extractSource(ele));
  33. return bd;
  34. }
  35. catch (ClassNotFoundException ex) {
  36. error( "Bean class [" + className + "] not found", ele, ex);
  37. }
  38. catch (NoClassDefFoundError err) {
  39. error( "Class that bean class [" + className + "] depends on not found", ele, err);
  40. }
  41. catch (Throwable ex) {
  42. error( "Unexpected failure during bean definition parsing", ele, ex);
  43. }
  44. finally {
  45. this.parseState.pop();
  46. }
  47. return null;
  48. }

上面是具体生成BeanDefinition的地方。在这里举一个对property进行解析的例子来完成对整个

BeanDefinition载入过程的分析,还是在类BeanDefinitionParserDelegate的代码中,一层一层

的对BeanDefinition中的定义进行解析,比如从属性元素结合到具体的每一个属性元素,然后才道

具体值的处理。根据解析结果,对这些属性值的处理会被封装成PropertyValue对象并设置到

BeanDefinition对象中去。

BeanDefinition中property元素集合的处理:

BeanDefinitionParserDelegate.parsePropertyElements()方法入口


 
 
  1. public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
  2. // 获取bean元素下定义的所有节点
  3. NodeList nl = beanEle.getChildNodes();
  4. for ( int i = 0; i < nl.getLength(); i++) {
  5. Node node = nl.item(i);
  6. // 判断是property元素后对该property元素进行解析
  7. if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
  8. parsePropertyElement((Element) node, bd);
  9. }
  10. }
  11. }

BeanDefinitionParserDelegate.parsePropertyElement()方法property元素具体解析过程:


 
 
  1. public void parsePropertyElement(Element ele, BeanDefinition bd) {
  2. // 这里取得property的名字
  3. String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
  4. if (!StringUtils.hasLength(propertyName)) {
  5. error( "Tag 'property' must have a 'name' attribute", ele);
  6. return;
  7. }
  8. this.parseState.push( new PropertyEntry(propertyName));
  9. try {
  10. /**
  11. * 如果同一个Bean中已经有同名的property存在,则不进行解析,直接返回。
  12. * 如果再同一个Bean中有同名的property设置,那么起作用的只是第一个。
  13. */
  14. if (bd.getPropertyValues().contains(propertyName)) {
  15. error( "Multiple 'property' definitions for property '" + propertyName + "'", ele);
  16. return;
  17. }
  18. /**
  19. * 这里是解析property值的地方,返回的对象对应Bean定义的property属性设置的解析结果,
  20. * 这个解析结果会封装到PropertyValue对象中,然后设置。
  21. */
  22. Object val = parsePropertyValue(ele, bd, propertyName);
  23. PropertyValue pv = new PropertyValue(propertyName, val);
  24. parseMetaElements(ele, pv);
  25. pv.setSource(extractSource(ele));
  26. bd.getPropertyValues().addPropertyValue(pv);
  27. }
  28. finally {
  29. this.parseState.pop();
  30. }
  31. }

BeanDefinitionParserDelegate.parsePropertyValue()方法解析property的value值:


 
 
  1. public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
  2. String elementName = (propertyName != null) ?
  3. "<property> element for property '" + propertyName + "'" :
  4. "<constructor-arg> element";
  5. // Should only have one child element: ref, value, list, etc.
  6. NodeList nl = ele.getChildNodes();
  7. Element subElement = null;
  8. for ( int i = 0; i < nl.getLength(); i++) {
  9. Node node = nl.item(i);
  10. if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
  11. !nodeNameEquals(node, META_ELEMENT)) {
  12. // Child element is what we're looking for.
  13. if (subElement != null) {
  14. error(elementName + " must not contain more than one sub-element", ele);
  15. }
  16. else {
  17. subElement = (Element) node;
  18. }
  19. }
  20. }
  21. // 判断Property的属性,是ref还是value,不允许同时出现ref和value
  22. boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
  23. boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
  24. if ((hasRefAttribute && hasValueAttribute) ||
  25. ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
  26. error(elementName +
  27. " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
  28. }
  29. // 如果是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref的信息
  30. if (hasRefAttribute) {
  31. String refName = ele.getAttribute(REF_ATTRIBUTE);
  32. if (!StringUtils.hasText(refName)) {
  33. error(elementName + " contains empty 'ref' attribute", ele);
  34. }
  35. RuntimeBeanReference ref = new RuntimeBeanReference(refName);
  36. ref.setSource(extractSource(ele));
  37. return ref;
  38. }
  39. // 如果是value,创建一个value的数据对象TypedStringValue,这个对象封装了value的信息
  40. else if (hasValueAttribute) {
  41. TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
  42. valueHolder.setSource(extractSource(ele));
  43. return valueHolder;
  44. }
  45. // 如果还有子元素,触发对子元素的解析
  46. else if (subElement != null) {
  47. return parsePropertySubElement(subElement, bd);
  48. }
  49. else {
  50. // Neither child element nor "ref" or "value" attribute found.
  51. error(elementName + " must specify a ref or value", ele);
  52. return null;
  53. }
  54. }

这里对property子元素的解析过程,Array、List、Set、Map、Prop等各种元素都会在这里解析,生成对应的数据对象,

比如ManagedList、ManagedArray、ManagedSet等等。这些ManagedXX类是Spring对具体的BeanDefinition的

数据封装。具体的解析可以从parsePropertySubElement()关于property子元素的解析深入追踪,可以看到

parseArrayElement、parseListElement、parseSetElement、parseMapElement、parsePropsElement等方法处理。

BeanDefinitionParserDelegate.parsePropertySubElement()方法子元素解析源码:


 
 
  1. public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
  2. return parsePropertySubElement(ele, bd, null);
  3. }

 
 
  1. public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
  2. if (!isDefaultNamespace(ele)) {
  3. return parseNestedCustomElement(ele, bd);
  4. }
  5. else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
  6. BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
  7. if (nestedBd != null) {
  8. nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
  9. }
  10. return nestedBd;
  11. }
  12. else if (nodeNameEquals(ele, REF_ELEMENT)) {
  13. // A generic reference to any name of any bean.
  14. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
  15. boolean toParent = false;
  16. if (!StringUtils.hasLength(refName)) {
  17. // A reference to the id of another bean in the same XML file.
  18. refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
  19. if (!StringUtils.hasLength(refName)) {
  20. // A reference to the id of another bean in a parent context.
  21. refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
  22. toParent = true;
  23. if (!StringUtils.hasLength(refName)) {
  24. error( "'bean', 'local' or 'parent' is required for <ref> element", ele);
  25. return null;
  26. }
  27. }
  28. }
  29. if (!StringUtils.hasText(refName)) {
  30. error( "<ref> element contains empty target attribute", ele);
  31. return null;
  32. }
  33. RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
  34. ref.setSource(extractSource(ele));
  35. return ref;
  36. }
  37. else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
  38. return parseIdRefElement(ele);
  39. }
  40. else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
  41. return parseValueElement(ele, defaultValueType);
  42. }
  43. else if (nodeNameEquals(ele, NULL_ELEMENT)) {
  44. // It's a distinguished null value. Let's wrap it in a TypedStringValue
  45. // object in order to preserve the source location.
  46. TypedStringValue nullHolder = new TypedStringValue( null);
  47. nullHolder.setSource(extractSource(ele));
  48. return nullHolder;
  49. }
  50. else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
  51. return parseArrayElement(ele, bd);
  52. }
  53. else if (nodeNameEquals(ele, LIST_ELEMENT)) {
  54. return parseListElement(ele, bd);
  55. }
  56. else if (nodeNameEquals(ele, SET_ELEMENT)) {
  57. return parseSetElement(ele, bd);
  58. }
  59. else if (nodeNameEquals(ele, MAP_ELEMENT)) {
  60. return parseMapElement(ele, bd);
  61. }
  62. else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
  63. return parsePropsElement(ele);
  64. }
  65. else {
  66. error( "Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
  67. return null;
  68. }
  69. }

看看一个List这样的睡醒配置是怎样被解析的?

BeanDefinitionParserDelegate.parseListElement()方法List属性解析源码:


 
 
  1. public List<Object> parseListElement(Element collectionEle, BeanDefinition bd) {
  2. String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
  3. NodeList nl = collectionEle.getChildNodes();
  4. ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
  5. target.setSource(extractSource(collectionEle));
  6. target.setElementTypeName(defaultElementType);
  7. target.setMergeEnabled(parseMergeAttribute(collectionEle));
  8. // 具体的List元素解析过程
  9. parseCollectionElements(nl, target, bd, defaultElementType);
  10. return target;
  11. }

BeanDefinitionParserDelegate.parseCollectionElements()方法List属性具体的解析源码:


 
 
  1. protected void parseCollectionElements(
  2. NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
  3. // 遍历所有的元素节点,并判断其类型是否为Element
  4. for ( int i = 0; i < elementNodes.getLength(); i++) {
  5. Node node = elementNodes.item(i);
  6. if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
  7. //加入到Target中国,target是一个ManagedList,同时触发对下一层子元素的解析过程,
  8. // 这是一个递归的调用,逐层地解析
  9. target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
  10. }
  11. }
  12. }

    经过逐层地解析,我们在XML文件中定义的BeanDefinition就被整个载入到了IOC容器中,

并在容器中建立了数据映射。在IOC容器中建立了对应的数据结构,或者可以看成是POJO对象

在IOC容器中的抽象,这些数据结构可以以AbstracBeanDefinition为入口,让IOC容器执行索引、

查询和操作。

    每一个简单的POJO操作的背后其实都含着一个复杂的抽象过程,经过以上的载入过程,IOC容器

大致完成了管理Bean对象的数据准备工作也即是数据初始化过程。严格来说,这个时候容器还没有起作用,

要完全发挥容器的作用,还需要完成数据向容器的注册,也即IOC容器注册BeanDefinition。

参考文献:

1、《Spring技术内幕》

2、《Spring实战》

3、Spring官网API

4、Spring源码

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值