Spring源码5.0:标签的解析

默认标签的解析

bean标签的解析及注册

Spring中的标签包括默认标签和自定义标签两种。
其两种标签的用法以及解析方式存在着很大的不同
默认标签是在parseDefaultElement函数中进行的。分别对不同的标签(import,bean,alias和beans)做了不同的处理

DefaultBeanDefinitionDocumentReader.class

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));
   }
}
  • 先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder。由此方法后,bdHodler已经包含了我们配置文件中配置的各种属性了。eg.class,id,name,alias之类的属性。
  • 当返回bdHolder不为空时,若默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。
  • 解析完成后需要对解析后的bdHolder进行注册,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法。
  • 最后发出相应事件,通知相关的监听器,这个bean已经加载完成了

时序图
avatar

解析BeanDefinition

从元素解析及信息提取开始,即BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)

BeanDefinitionParserDelegate.class

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null);
}

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
   String id = ele.getAttribute(ID_ATTRIBUTE);
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

   List<String> 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;
}

上代码能看到id和name的解析。

  • 提取元素的id以及name属性
  • 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中
  • 如果检测到bean没有指定beanName,那么使用默认规则为此bean生成beanName
  • 将获取到的信息封装到BeanDefinitionHolder的实例中

对标签其他属性的解析过程
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, @Nullable BeanDefinition containingBean) {

   this.parseState.push(new BeanEntry(beanName));

   String className = null;
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }
   String parent = null;
   if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
      parent = ele.getAttribute(PARENT_ATTRIBUTE);
   }

   try {
      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;
}

bean标签的所有属性都能由上看到。

创建用于属性承载的BeanDefinition

  • BeanDefinition是一个接口。
    在Spring中存在三种实现:RootBeanDefinition,ChildBeanDefiniton,GenericBeanDefinitioin。
    三种实现均继承了AbstractBeanDefintion。
    BeanDefinition是配置文件元素标签在容器中的内部表示形式。元素标签拥有class。scope,lazy-init等配置属性。BeanDefintion则提供了相应的beanClass,scope,lazyInit属性。
    BeanDefinitoin和中的属性是一一对应的。
    其中RootBeanDefinition是最常用的实现类。它对应一般性元素标签。
    GenericBeanDefinition是自2.5版本以后加入的bean文件配置属性定义类。是一站式服务类。
  • 配置文件中可以定义父,子,父 用RootBeanDefintion表示。子用ChildBeanDefiniton表示,而没有父的就使用RootBeanDefintion表示。
    AbstractBeanDefinition对两者共同的类信息进行抽象。
    -Spring通过BeanDefintion将配置文件中的配置信息转换为容器的内部表示。并将这些BeanDefintion注册到BeanDefinitionRegistry中
    Spring容器的BeanDefinitionRegistry就像是Spring配置新的内存数据库,主要以map的形式保存。后续从BeanDefinitionRegistry读取配置信息。
    avatar
    由此得,要解析属性首先要创建用于承载属性得实例,也就是创建GenericBeanDeifinition类型的实例。createBeanDefinition实现此功能

BeanDefinitionParserDelegate.class

protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
      throws ClassNotFoundException {

   return BeanDefinitionReaderUtils.createBeanDefinition(
         parentName, className, this.readerContext.getBeanClassLoader());
}

BeanDefinitionReaderUtils.class

public static AbstractBeanDefinition createBeanDefinition(
      @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

   GenericBeanDefinition bd = new GenericBeanDefinition();
   bd.setParentName(parentName);
   if (className != null) {
      if (classLoader != null) {
         bd.setBeanClass(ClassUtils.forName(className, classLoader));
      }
      else {
         bd.setBeanClassName(className);
      }
   }
   return bd;
}

解析各种属性

创建完bean信息的承载实例后,便可以进行bean信息的各种属性解析。
BeanDefinitionParserDelegate方法是对element所有元素属性进行解析。

BeanDefinitionParserDelegate.class

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
      @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

   if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
      error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
   }
   else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
      bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
   }
   else if (containingBean != null) {
      // Take default from containing bean in case of an inner bean definition.
      bd.setScope(containingBean.getScope());
   }

   if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
      bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
   }

   String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
   if (isDefaultValue(lazyInit)) {
      lazyInit = this.defaults.getLazyInit();
   }
   bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

   String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
   bd.setAutowireMode(getAutowireMode(autowire));

   if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
      String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
      bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
   }

   String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
   if (isDefaultValue(autowireCandidate)) {
      String candidatePattern = this.defaults.getAutowireCandidates();
      if (candidatePattern != null) {
         String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
         bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
      }
   }
   else {
      bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
   }

   if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
      bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
   }

   if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
      String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
      bd.setInitMethodName(initMethodName);
   }
   else if (this.defaults.getInitMethod() != null) {
      bd.setInitMethodName(this.defaults.getInitMethod());
      bd.setEnforceInitMethod(false);
   }

   if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
      String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
      bd.setDestroyMethodName(destroyMethodName);
   }
   else if (this.defaults.getDestroyMethod() != null) {
      bd.setDestroyMethodName(this.defaults.getDestroyMethod());
      bd.setEnforceDestroyMethod(false);
   }

   if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
      bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
   }
   if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
      bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
   }

   return bd;
}

解析子元素meta

元数据meta使用:

<bean id ="myTestBean" class="bean.MyTesBean">
<meta key = "testStr" value="aa"/>
</bean>

此代码不会体现在MyTestBean的属性中,只是作为额外的声明
通过BeanDefinition的getAttribute(key)方法进行获取
BeanDefinitionParserDelegate.class

public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
   NodeList nl = ele.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
         Element metaElement = (Element) node;
         String key = metaElement.getAttribute(KEY_ATTRIBUTE);
         String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
         BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
         attribute.setSource(extractSource(metaElement));
         attributeAccessor.addMetadataAttribute(attribute);
      }
   }
}

解析子元素lookup-method

获取器注入
直接调用,其抽象方法由其xml配置实现

<bean id="getBeanTest" class="test.lookup.app.GetBeanTest">
 <lookup-method name="getBean" bean ="student"/>

BeanDefinitionParserDelegate.class

public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
         Element ele = (Element) node;
         String methodName = ele.getAttribute(NAME_ATTRIBUTE);
         String beanRef = ele.getAttribute(BEAN_ELEMENT);
         LookupOverride override = new LookupOverride(methodName, beanRef);
         override.setSource(extractSource(ele));
         overrides.addOverride(override);
      }
   }
}

解析子元素replaced-method

方法替换
BeanDefinitionParserDelegate.class

public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
         Element replacedMethodEle = (Element) node;
         String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
         String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
         ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
         // Look for arg-type match elements.
         List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
         for (Element argTypeEle : argTypeEles) {
            String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
            match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
            if (StringUtils.hasText(match)) {
               replaceOverride.addTypeIdentifier(match);
            }
         }
         replaceOverride.setSource(extractSource(replacedMethodEle));
         overrides.addOverride(replaceOverride);
      }
   }
}

不论是look-up还是replaced-method都是构造了一个MethodOverride。并最终记录在了AbstractBeanDefinition中的methodOverrides属性中

constructor-arg

构造函数

<bean id="student" class="com.rc.sp.Student">
  <constructor-arg name="id" value="1"/>
   <constructor-arg name="name" value="student"/>
   <constructor-arg name="dream">
        <list>
            <value>soldier</value>
            <value>scientist</value>
            <value>pilot</value>
        </list>
    </constructor-arg>
    <constructor-arg name="score">
        <map>
            <entry key="math" value="90"/>
            <entry key="english" value="85"/>
        </map>
    </constructor-arg>
    <constructor-arg name="graduation" value="false"/>
</bean>

先是对于constructor-arg子元素的解析。通过parseConstructorArgElements函数实现
BeanDefinitionParserDelegate.class

public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
         parseConstructorArgElement((Element) node, bd);
      }
   }
}

先是遍历所有子元素提取所有的constructor-arg
再由另个函数parseConstructorArgElements来具体解析
BeanDefinitionParserDelegate.class

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));
//解析ele对于的属性元素
               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();
      }
   }
}

先提取constructor-arg上必要的属性(index,type,name)
如果配置指定了index属性

  • 解析Constructor-arg的子元素
  • 使用ConstructorArgumentValues.ValueHolder 类型封装解析出来的元素
  • 将type,name,index属性一起封装在ConstructorArgumentValues.ValueHolder 类型中并添加至当前BeanDefinition的ConstructorArgumentValues的IndexedArgumentValue属性中

若没有index属性

  • 解析Constructor-arg的子元素
  • 使用ConstructorArgumentValues.ValueHolder 类型封装解析出来的元素
  • 将type,name,index属性一起封装在ConstructorArgumentValues.ValueHolder 类型中并添加至当前BeanDefinition的ConstructorArgumentValues的genericArgumentValue属性中

解析ele对于的属性元素
BeanDefinitionParserDelegate.class

@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
   String elementName = (propertyName != null ?
         "<property> element for property '" + propertyName + "'" :
         "<constructor-arg> element");

   // Should only have one child element: ref, value, list, etc.
   NodeList nl = ele.getChildNodes();
   Element subElement = null;
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
            !nodeNameEquals(node, META_ELEMENT)) {
         // Child element is what we're looking for.
         if (subElement != null) {
            error(elementName + " must not contain more than one sub-element", ele);
         }
         else {
            subElement = (Element) node;
         }
      }
   }

   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) {
      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) {
      TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
      valueHolder.setSource(extractSource(ele));
      return valueHolder;
   }
   else if (subElement != null) {
      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;
   }
}
  • 略过description或者meta
  • 提取constructor-arg 上的ref和value元素,以便根据规则验证正确性,其会在为在constructor-arg上不存在一下情况
    同时有ref和value
    存在reforvalue属性且又有子元素
  • ref属性的处理 RuntimeBeanReference封装对应的ref名称
  • value的处理 TypedStringValue封装
  • 子元素的处理 parsePropertySubElement

property

parsePropertyElement函数完成了对property属性的提取。

public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
         parsePropertyElement((Element) node, bd);
      }
   }
}
public void parsePropertyElement(Element ele, BeanDefinition bd) {
   String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
   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);
      PropertyValue pv = new PropertyValue(propertyName, val);
      parseMetaElements(ele, pv);
      pv.setSource(extractSource(ele));
      bd.getPropertyValues().addPropertyValue(pv);
   }
   finally {
      this.parseState.pop();
   }
}

可以看到上面函数与构造函数注入方式不同的是将返回值使用Property Vaule进行封装,并记录在了BeanDefinition中的orioerty Values属性中

解析qualifier

对于qualifier元素的获取更多的是使用注解的形式。
在使用Spring自定注入时,Spring容器中匹配的候选Bean数目必须有且仅有一个。
使用Qualifiler注入Bean名称时,这样歧义就消除了。

自定义标签解析

当完成从配置文件到Document的转换并提取对于root后。
开始了所有元素的解析。便开始了默认,自定义区分
DefaultBeanDefinitionDocumentReader.class

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);
   }
}

以下是围绕 delegate.parseCustomElement(root)开展。
当Spring拿到一个元素时,首先根据命名空间进行解析,如果未默认,则使用parseDefaultElement方法进行解析。否则使用parseCustomElement方法进行解析。

自定义标签使用

步骤

  • 创建一个需要扩展的组件
  • 定义一个XSD文件表示组件内容
  • 创建一个文件,实现BeanDefintionParser接口,用于解析XSD文件中的定义和组件定义
  • 创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器。
  • 编写 Spring.handlers和Spring.schemas文件

详细实现网上案例

自定义标签解析

@Nullable
public BeanDefinition parseCustomElement(Element ele) {
   return parseCustomElement(ele, null);
}

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
   String namespaceUri = getNamespaceURI(ele);
   if (namespaceUri == null) {
      return null;
   }
   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));
}
  • 根据对于的bean获取对应的命名空间,根据命名空间解析对应处理器
  • 根据用户自定义的处理器进行解析

获取标签命名空间

标签的解析是从命名空间的提起开始
区分默认和自定义都是以标签所提供的命名空间为基础的。
如何提取已经由org.w3c.dom.Node提供方法直接调用

public String getNamespaceURI(Node node) {
   return node.getNamespaceURI();
}

提取自定义标签处理器

有了命名空间后就可以进行NamespaceHandler的提取了。
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
在readerContext初始化的时候其属性NamespaceHandlerResolver 已经被初始化为了DefaultNamespaceHandlerResolver的实例。
这里调用的resolve方法其实为DefaultNamespaceHandlerResolver类中的方法。

DefaultNamespaceHandlerResolver.class

public NamespaceHandler resolve(String namespaceUri) {
   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);
         if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
            throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                  "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
         }
         NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
         namespaceHandler.init();
         handlerMappings.put(namespaceUri, namespaceHandler);
         return namespaceHandler;
      }
      catch (ClassNotFoundException ex) {
         throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
               "] for namespace [" + namespaceUri + "]", ex);
      }
      catch (LinkageError err) {
         throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
               className + "] for namespace [" + namespaceUri + "]", err);
      }
   }
}

上述函数解析自定义NamespaceHandler的过程。
如果要使用自定义标签,其中必不可少的操作就是在Spring.handlers文件中配置命名空间与命名处理器的映射关系。
如此Spring才能根据映射关系找到匹配的处理器,而寻找匹配处理器就是在上面函数实现的。
获取到自定义NamespaceHandler后就可以进行处理器初始化并解析了。

得到自定义命名空间后马上执行namespaceHandler.init() 来进行自定义BeanDefinitionParser的注册。可注册多个标签解析器。
注册后,命名空间处理器就可以根据标签的不同来调用不同的解析器进行解析。
getHandlerMappings的主要功能就是读取Spring.handlers配置文件并将配置文件缓存在map中

private Map<String, Object> getHandlerMappings() {
   Map<String, Object> handlerMappings = this.handlerMappings;
   if (handlerMappings == null) {
      synchronized (this) {
         handlerMappings = this.handlerMappings;
         if (handlerMappings == null) {
            if (logger.isDebugEnabled()) {
               logger.debug("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
            }
            try {
               Properties mappings =
                     PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
               if (logger.isDebugEnabled()) {
                  logger.debug("Loaded NamespaceHandler mappings: " + mappings);
               }
               handlerMappings = new ConcurrentHashMap<>(mappings.size());
               CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
               this.handlerMappings = handlerMappings;
            }
            catch (IOException ex) {
               throw new IllegalStateException(
                     "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
            }
         }
      }
   }
   return handlerMappings;
}

借助了工具类PropertiesLoaderUtils对属性handlerMappingsLocation进行了配置文件的读取。
handlerMappingsLocation被默认初始化为"META-INF/Spring.handlers"

标签解析

得到解析器以及分析的元素后。
Sping将解析工作委托给自定义解析器去解析了。
return handler.parse(ele,new ParserContext(this.readerCOntexxt,this,containingBd)
此时handler已经实例化成我们自定义的handler了。
NamespaceHandler也完成了初始化的工作。
自定义命名空间处理器并没有实现parse方法。
NamespaceHandlerSupport.class

public BeanDefinition parse(Element element, ParserContext parserContext) {
   BeanDefinitionParser parser = findParserForElement(element, parserContext);
   return (parser != null ? parser.parse(element, parserContext) : null);
}


private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
   String localName = parserContext.getDelegate().getLocalName(element);
   BeanDefinitionParser parser = this.parsers.get(localName);
   if (parser == null) {
      parserContext.getReaderContext().fatal(
            "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
   }
   return parser;
}

parse的处理
AbstractBeanDefinitionParser.class


public final BeanDefinition parse(Element element, ParserContext parserContext) {
   AbstractBeanDefinition definition = parseInternal(element, parserContext);
   if (definition != null && !parserContext.isNested()) {
      try {
         String id = resolveId(element, definition, parserContext);
         if (!StringUtils.hasText(id)) {
            parserContext.getReaderContext().error(
                  "Id is required for element '" + parserContext.getDelegate().getLocalName(element)
                        + "' when used as a top-level tag", element);
         }
         String[] aliases = null;
         if (shouldParseNameAsAliases()) {
            String name = element.getAttribute(NAME_ATTRIBUTE);
            if (StringUtils.hasLength(name)) {
               aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
            }
         }
         BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
         registerBeanDefinition(holder, parserContext.getRegistry());
         if (shouldFireEvents()) {
            BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
            postProcessComponentDefinition(componentDefinition);
            parserContext.registerComponent(componentDefinition);
         }
      }
      catch (BeanDefinitionStoreException ex) {
         String msg = ex.getMessage();
         parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
         return null;
      }
   }
   return definition;
}

大部分是用来处理解析后的AbstractBeanDefinitoin转化为BeanDeifnitionHolder并注册的功能。
而真正委托交给了函数parseInternal。
正是这句代码调用了我们自定义的解析函数,
parseInternal并非直接调用自定义doparse,而是进行了一系列数据准备,包括beanClass,scope等属性的准备。

AbstractSingleBeanDefinitionParser.class

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
   BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
   String parentName = getParentName(element);
   if (parentName != null) {
      builder.getRawBeanDefinition().setParentName(parentName);
   }
   Class<?> beanClass = getBeanClass(element);
   if (beanClass != null) {
      builder.getRawBeanDefinition().setBeanClass(beanClass);
   }
   else {
      String beanClassName = getBeanClassName(element);
      if (beanClassName != null) {
         builder.getRawBeanDefinition().setBeanClassName(beanClassName);
      }
   }
   builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
   BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
   if (containingBd != null) {
      // Inner bean definition must receive same scope as containing bean.
      builder.setScope(containingBd.getScope());
   }
   if (parserContext.isDefaultLazyInit()) {
      // Default-lazy-init applies to custom bean definitions as well.
      builder.setLazyInit(true);
   }
   doParse(element, parserContext, builder);
   return builder.getBeanDefinition();
}

至此完成了Spring的解析工作,即
Spring将bean配置文件加载到内存的全部过程。(包括下一节的注册。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值