博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring读书笔记——bean解析
阅读量:6995 次
发布时间:2019-06-27

本文共 13547 字,大约阅读时间需要 45 分钟。

前情回顾

上篇《Spring读书笔记——bean加载》我们从代码角度介绍了有哪些类负责解析XML文件,又是如何一步步从XML格式脱变成我们熟悉的bean的,直到DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法。我们抽丝剥茧,终于快看到Spring是如何解析bean的代码了。

在此之前,我们回一下上篇看到过的主要类和方法
XmlBeanFactory(XmlFactory) ->
XmlBeanDefinitionReader(loadBeanDefinitions)->
XmlBeanDefinitionReader(doLoadBeanDefinitions)->
XmlBeanDefintionReader(registerBeanDefinitions)
DefaultBeanDefinitionDocumentReader(registerBeanDefinitions)->
DefaultBeanDefinitonDocumentReader(doRegisterBeanDefinitions)

解析的类型

Spring的标签分为默认标签和自定义标签两种。

默认标签主要包括:import、alias、bean和beans
自定义标签:顾名思义就是自定义的标签,比如

DefaultBeanDefinitionDocumentReader

从XmlBeanDefinitionReader的registerBeanDefinitions开始,我们第一步要做的就是创建一个Xml文档的解析类

BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

createBeanDefinitionDocumentReader方法创建了类DefaultBeanDefinitionDocumentReader。

从DefaultBeanDefinitionDocumentReader的registerBeanDefinitions到doRegisterBeanDefinitions,下面我们着重从doRegisterBeanDefinitions的parseBeanDefintions方法开始往下看。

代理类BeanDefinitionParserDelegate

负责解析bean的并不是DefaultBeanDefinitionDocumentReader,而是委托给了代理类BeanDefinitionParserDelegate,后面我们会看到BeanDefinitionParserDelegate这个功臣是如何施展拳脚解析bean的。

parseBeanDefinitions(root, this.delegate);

进入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);            }            else {               delegate.parseCustomElement(ele);            }         }      }   }   else {      delegate.parseCustomElement(root);   }}
  • 判断是否是默认命名空间的,如果是则遍历root下所有的子标签,如果不是,则进行自定义解析
  • 对于是默认命名空间下的标签,在遍历得到每一个子标签后开始调用parseDefaultElement方法开始我们之前提到的默认标签的解析。

默认标签解析

前面已经提到过默认标签有四种,我们看看代码也是一目了然,下面是解析默认标签的parseDefaultElement的方法实现

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {      importBeanDefinitionResource(ele);   }   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {      processAliasRegistration(ele);   }   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {      processBeanDefinition(ele, delegate);   }   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {      // recurse      doRegisterBeanDefinitions(ele);   }}
  • if...else语句分别判定了import、alias、bean和beans的情况,并且不同情况作了不同的方法实现
  • 首先要看的就是我们再熟悉不过的bean标签的解析processBeanDefinition方法

bean标签的解析

processBeanDefinition的方法实现如下

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);   if (bdHolder != null) {      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开始排上用场,用于对bean元素解析,并得到BeanDefinitionHolder类型的bdHolder对象
  • 对于解析得到结果不为空的情况,首先还要对于需要修饰的情况(默认标签包含自定义标签)进行处理
  • 然后借助BeanDefinitionReaderUtils的registerBeanDefinition对bdHolder进行注册
  • 最后发送响应事件,通知响应的监听器(Spring3x版本这块还没有具体实现,只是留了个壳)

delegate.parseBeanDefinitionElement

我们主要从processBeanDefintion的第一行代码开始,来到BeanDefinitionParserDelegate.parseBeanDefinitionElement

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {   String id = ele.getAttribute(ID_ATTRIBUTE);   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);   List
aliases = new ArrayList
(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; 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"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null;}
  • 有很多我们之前熟知的细节从这里的代码就可以找到根据,比如“bean如果不声明name属性,spring会默认创建一个name”,显然通过上面一点代码我们就看到Spring确实是这么实现的,而且我们看到如果没有声明name,默认的name值就是id的值
  • 紧接着,我们看下
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);这行代码,进入parseBeanDefinitionElement我们会看到这个方法做了很多的解析的工作,比如解析parent属性、解析元数据、解析构造函数、解析property子元素等等

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);      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));      parseMetaElements(ele, bd);      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());      parseConstructorArgElements(ele, bd);      parsePropertyElements(ele, bd);      parseQualifierElements(ele, bd);      bd.setResource(this.readerContext.getResource());      bd.setSource(extractSource(ele));      return bd;   }   catch (ClassNotFoundException ex) {      error("Bean class [" + className + "] not found", ele, ex);   }   catch (NoClassDefFoundError err) {      error("Class that bean class [" + className + "] depends on not found", ele, err);   }   catch (Throwable ex) {      error("Unexpected failure during bean definition parsing", ele, ex);   }   finally {      this.parseState.pop();   }   return null;}

我们挑其中一个解析属性函数parseConstructorArgElements看下具体实现。

parseConstructorArgElements用于解析ConstructArg标签。
我们常见的construct-arg大概是这样

​ ​
jackie
male

construct-arg标签的解析过程如下所示

public void parseConstructorArgElement(Element ele, BeanDefinition bd) {   String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);   String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);   if (StringUtils.hasLength(indexAttr)) {      try {         int index = Integer.parseInt(indexAttr);         if (index < 0) {            error("'index' cannot be lower than 0", ele);         }         else {            try {               this.parseState.push(new ConstructorArgumentEntry(index));               Object value = parsePropertyValue(ele, bd, null);               ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);               if (StringUtils.hasLength(typeAttr)) {                  valueHolder.setType(typeAttr);               }               if (StringUtils.hasLength(nameAttr)) {                  valueHolder.setName(nameAttr);               }               valueHolder.setSource(extractSource(ele));               if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {                  error("Ambiguous constructor-arg entries for index " + index, ele);               }               else {                  bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);               }            }            finally {               this.parseState.pop();            }         }      }      catch (NumberFormatException ex) {         error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);      }   }   else {      try {         this.parseState.push(new ConstructorArgumentEntry());         Object value = parsePropertyValue(ele, bd, null);         ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);         if (StringUtils.hasLength(typeAttr)) {            valueHolder.setType(typeAttr);         }         if (StringUtils.hasLength(nameAttr)) {            valueHolder.setName(nameAttr);         }         valueHolder.setSource(extractSource(ele));         bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);      }      finally {         this.parseState.pop();      }   }}
  • 如果constrcut-arg没有指定index函数,默认按照声明属性的顺序来解析,如果声明则按照index的值解析
  • 使用ConstrcutorArgumentValues的ValueHolder来封装解析得到的type、name等属性值
  • 最后将上面的valueHolder对象按照index添加到BeanDefinition的indexedArgument属性上
  • 注意,对于没有index声明的,最终解析后是存放到genericArgumentValues属性上

bean标签的注册

上面层层剥开,一直到解析到具体的属性,我们现在再一层层的往回走。前面所做的工作,无非是将Xml的元素解析出来并与Spring中的BeanDefinition,也就是换了一种形式在Spring中存在。

我们最终得到的是BeanDefinitionHolder

public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, String[] aliases) {   Assert.notNull(beanDefinition, "BeanDefinition must not be null");   Assert.notNull(beanName, "Bean name must not be null");   this.beanDefinition = beanDefinition;   this.beanName = beanName;   this.aliases = aliases;}

解析转换工作完成了,现在需要开始bean的注册了

我们回到DefaultBeanDefinitionDocumentReader的processBeanDefinition,看看注册是如何实现的。

从DefaultBeanDefinitionDocumentReader.processBeanDefinition->BeanDefinitionReaderUtils.registerBeanDefinition->DefaultLisableBeanFactory.registerBeanFactory。

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)      throws BeanDefinitionStoreException {   Assert.hasText(beanName, "Bean name must not be empty");   Assert.notNull(beanDefinition, "BeanDefinition must not be null");   if (beanDefinition instanceof AbstractBeanDefinition) {      try {         ((AbstractBeanDefinition) beanDefinition).validate();      }      catch (BeanDefinitionValidationException ex) {         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,               "Validation of bean definition failed", ex);      }   }   synchronized (this.beanDefinitionMap) {      Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);      if (oldBeanDefinition != null) {         if (!this.allowBeanDefinitionOverriding) {            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,                  "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +                  "': There is already [" + oldBeanDefinition + "] bound.");         }         else {            if (this.logger.isInfoEnabled()) {               this.logger.info("Overriding bean definition for bean '" + beanName +                     "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");            }         }      }      else {         this.beanDefinitionNames.add(beanName);         this.frozenBeanDefinitionNames = null;      }      this.beanDefinitionMap.put(beanName, beanDefinition);   }   resetBeanDefinition(beanName);}
  • 如果beanName已经注册又没有声明可以覆盖,则会抛出异常
  • 符合条件的bean最终会被加入一个map结合
  • map集合的key是beanName,value就是前面解析得到的BeanDefinition实例对象
  • 清楚解析之前留下对应的beanName的缓存

至此,我们看到了Spring是如何解析标签(我们还有自定义标签没说),如何解析bean标签(我们还有import、alias和beans没说),如何解析标签属性(我们还有太多的属性没有介绍,但是都是类似的过程),最终完成了bean的注册。

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

1240

转载于:https://www.cnblogs.com/bigdataZJ/p/SpringSource2.html

你可能感兴趣的文章
每天自动发英文外链 247backlinks
查看>>
[收藏]书籍推荐
查看>>
ipad 、iphone开发-通过定时器显示进度条
查看>>
综合知识总结
查看>>
【技术贴】PL/SQL错误提示 database character set(AL32UTF8) a
查看>>
Oracle学习<四>
查看>>
英语字根
查看>>
ADO.NET - 1.基础(SqlCommand\ExecuteScalar\ExecuteReader\sqlDataAdapter)
查看>>
归并排序模板
查看>>
C#技术分享【PDF转换成图片——11种方案】
查看>>
python网络编程学习笔记(6):Web客户端访问
查看>>
Oracle pctfree,pctused,initrans,maxtrans
查看>>
The 32-bit general-purpose registers EAX, EBX, ECX, EDX, ESI, EDI, EBP, and ESP
查看>>
建议加入的几个国外程序员社区
查看>>
c#页面无内容解决方案
查看>>
java命令执行jar包的方式
查看>>
IBM RSA (IBM rational software architect ) V8 学习之四 C++类图转换成c++代码
查看>>
[转]linux netstat命令查看端口是否占用
查看>>
【java】【转】HQL查询语句
查看>>
Spring中使用CXF
查看>>