首页 > 编程语言 > 详细

spring默认标签解析

时间:2020-02-20 20:22:34      阅读:75      评论:0      收藏:0      [点我收藏+]

 

B、解析默认bean的各种属性

默认bean的解析切入函数是parseBeanDefinitionAttributes

/**
 * 硬编码解析默认属性
 */
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
      @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
   //获取bean的作用域
   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)));
   }

   //是否为懒加载  默认为false
   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));

   //解析depends-on属性
   if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
      String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
      bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
   }
   //解析autowire-candidate属性
   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));
   }
   //解析primary属性
   if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
      bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
   }
   //解析init-method属性
   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);
   }
//解析destroy-method属性
   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);
   }
   //解析factory-method属性
   if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
      bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
   }
   //解析factory-bean属性
   if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
      bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
   }

   return bd;
}

 

C、解析元数据

元数据配置xml实例:

<bean id="test" name="testA,testBean" class="test.TestBean">
   <meta key="name" value="aaaaaaa"></meta>
</bean>

获取实例meta元素的实例:

beanDefinition.getAttribute("name");

元数据的解析函数是parseMetaElements,具体实现细节如下:

public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
   NodeList nl = ele.getChildNodes();
   //遍历子节点
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      //判断是否为meta元素
      if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
         Element metaElement = (Element) node;
         //获取meta元素的key值
         String key = metaElement.getAttribute(KEY_ATTRIBUTE);
         //获取meta元素的value
         String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
         //创建存储对象
         BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
         //将解析结果注入beanDefination中
         attribute.setSource(extractSource(metaElement));
         attributeAccessor.addMetadataAttribute(attribute);
      }
   }
}

以上便是meta元素的解析过程,包含如下几个步骤:

1、硬编码获取meta元素的keyvalue

2、创建meta元素的存储对象,并将解析的结果封装进去;

3、将解析的结果注入到beanDefination中。

Psmeta元素并不会体现在bean的属性中,而是额外的声明,需要读取信息的时候可以通过beanDefinationgetAttribute(key)方法获取。

D、解析lookUp-method属性

首先创建一个父类接口:

public interface User {

   public void showMe();
}

创建一个实现类:

public class Teacher implements User {
   @Override
   public void showMe() {
      System.out.println("I am Teacher");
   }
}

创建调用的抽象类:

public abstract class GetBeanTest {
   public void showMe(){
      this.getBean().showMe();
   }

   public abstract User getBean();
}

配置xml

<bean id="getBeanTest" class="lookupmethod.GetBeanTest">
   <lookup-method name="getBean" bean="teacher"></lookup-method>
</bean>
<bean id="teacher" class="lookupmethod.Teacher"></bean>

创建调用视图:

public class Test {
   public static void main(String[] args) {
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
      GetBeanTest getBeanTest = applicationContext.getBean("getBeanTest",GetBeanTest.class);
      getBeanTest.showMe();
      System.out.println(getBeanTest.getBean());
   }
}

运行结果:

I am Teacher

lookupmethod.Teacher@200a570f

总结:1、利用此标签可以可以将抽象类中没有get方法教给配置文件进行配置,同时也解决了抽象类不能实例化的问题。

1、利用此标签可以用在设计有些可插拨的功能。

Spring采用和上面相同的手段解析lookup-method属性,实现细节如下:

public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      //判断是否为lookup-method标签
      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);
         //注入beanDefination
         override.setSource(extractSource(ele));
         overrides.addOverride(override);
      }
   }
}

E、解析replaced-method

replaced-method顾名思义是进行方法替换的一个标签,可以在运行时替换现有方法。

创建需要替换的类和方法:

public class TestChangeMethod {

   public void changeMe(){
      System.out.println("change me");
   }
}

创建新的方法:

public class TestMethodReplacer implements MethodReplacer {
   @Override
   public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
      System.out.println("我替换了" + method.getName() + "方法");
      return null;
   }
}

Ps:要使用replaced-method标签那么就需要使用约定的接口进行实例化,MethodReplacer 接口定义了替换的约定。使用参考如上。

Spring进行replaced-method标签解析的源码如下:

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

以上代码就是解析改标签的全过程,包含如下几个步骤:

1、判断是否为replaced-method标签

2、硬编码解析参数,并封装入ReplaceOverride

3、获取replaced-method标签下的子标签(替换方法的参数)

4、如果有那么也转入ReplaceOverride

5、将局部变量注入到beanDefination

F、解析构造函数参数

需要使用构造函数注入的bean

public class TestBean {

   private String name;

   private boolean isRight;

   private Date birthday;

   private int age;

   public TestBean(String name, boolean isRight, Date birthday, int age) {
      this.name = name;
      this.isRight = isRight;
      this.birthday = birthday;
      this.age = age;
   }

   @Override
   public String toString() {
      return "TestBean{" +
            "name=‘" + name + ‘\‘‘ +
            ", isRight=" + isRight +
            ", birthday=" + birthday +
            ", age=" + age +
            ‘}‘;
   }
}

Xml配置:

<bean id="testBean" class="constructorarg.TestBean">
   <constructor-arg name="name">
      <value>张三</value>
   </constructor-arg>
   <constructor-arg name="age">
      <value>10</value>
   </constructor-arg>
   <constructor-arg name="isRight">
      <value>true</value>
   </constructor-arg>
   <constructor-arg name="birthday">
      <!--内部bean 由于构造器注入是string 格式,在注入是如果不手动转换那么将无法注入-->
      <bean factory-bean="dateFormat" factory-method="parse">
         <constructor-arg value="2007-12-20"></constructor-arg>
      </bean>
   </constructor-arg>
</bean>
<bean id="dateFormat" class="java.text.SimpleDateFormat">
   <constructor-arg value="yyyy-MM-dd"></constructor-arg>
</bean>

源码如下:

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

Spring监测到有构造器注入时遍历所有的constructor-arg标签并将解析的工作交给parseConstructorArgElement进行完成。

parseConstructorArgElement函数如下:

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

解析逻辑:

1、分别获取index name type的配置

2、如果配置了index那么按照index配置的位置进行匹配构造函数的入参

2.1、检查index是否符合约定(>=0,如果不符合约定抛出error

2.2、记录解析动作记录到parseState

2.3、解析constructor-arg的子元素

2.4、使用index type ref 属性一并封装入ConstructorArgumentValues.ValueHolder类型中,并添加至当前beanDefinationConstructorArgumentValuesIndexedArgumentValue

3、如果未配置index

3.1、记录解析动作记录到parseState

3.2、解析constructor-arg的子元素

3.3、使用index type ref 属性一并封装入ConstructorArgumentValues.ValueHolder类型中,并添加至当前beanDefinationConstructorArgumentValuesGenericArgumentValue

 ps:是否采用index对于spring而言只存在信息保存的位置,解析过程时相同的

子元素解析过程如下:

@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.
   //一个constructor-arg只能允许有 ref, value, list, etc中的一个子元素
   NodeList nl = ele.getChildNodes();
   Element subElement = null;
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      //过滤掉meta、description
      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;
         }
      }
   }
   //获取ref属性
   boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
   //获取value属性
   boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
   //如果同时有ref、value给出error
   if ((hasRefAttribute && hasValueAttribute) ||
         ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
      error(elementName +
            " is only allowed to contain either ‘ref‘ attribute OR ‘value‘ attribute OR sub-element", ele);
   }
   //处理ref类型
   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;
   }
   //处理value
   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;
   }
}

PsValue属性和ref属性的封装对象不同。

其他标签是放入parsePropertySubElement函数中进行解析的,解析如下:

@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
   if (!isDefaultNamespace(ele)) {
      return parseNestedCustomElement(ele, bd);
   }
   //bean元素处理
   else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
      //直接调用parseBeanDefinitionElement函数进行处理
      BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
      if (nestedBd != null) {
         nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
      }
      return nestedBd;
   }
   //ref元素处理
   else if (nodeNameEquals(ele, REF_ELEMENT)) {
      // A generic reference to any name of any bean.
      String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
      boolean toParent = false;
      if (!StringUtils.hasLength(refName)) {
         // A reference to the id of another bean in a parent context.
         refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
         toParent = true;
         if (!StringUtils.hasLength(refName)) {
            error("‘bean‘ or ‘parent‘ is required for <ref> element", ele);
            return null;
         }
      }
      if (!StringUtils.hasText(refName)) {
         error("<ref> element contains empty target attribute", ele);
         return null;
      }
      RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
      ref.setSource(extractSource(ele));
      return ref;
   }
   //idref元素处理
   else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
      return parseIdRefElement(ele);
   }
   //value元素处理
   else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
      return parseValueElement(ele, defaultValueType);
   }
   //null元素处理
   else if (nodeNameEquals(ele, NULL_ELEMENT)) {
      // It‘s a distinguished null value. Let‘s wrap it in a TypedStringValue
      // object in order to preserve the source location.
      TypedStringValue nullHolder = new TypedStringValue(null);
      nullHolder.setSource(extractSource(ele));
      return nullHolder;
   }
   //以下为各种集合处理
   else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
      return parseArrayElement(ele, bd);
   }
   else if (nodeNameEquals(ele, LIST_ELEMENT)) {
      return parseListElement(ele, bd);
   }
   else if (nodeNameEquals(ele, SET_ELEMENT)) {
      return parseSetElement(ele, bd);
   }
   else if (nodeNameEquals(ele, MAP_ELEMENT)) {
      return parseMapElement(ele, bd);
   }
   else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
      return parsePropsElement(ele);
   }
   else {
      error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
      return null;
   }
}

Spring对各种不同的标签都做了详细的分类处理。

对于时间格式的参数进行构造注入时需要进行如下处理:

<bean id="dateFormat" class="java.text.SimpleDateFormat">
   <constructor-arg value="yyyy-MM-dd"></constructor-arg>
</bean>

<constructor-arg name="birthday">
   <!--内部bean 由于构造器注入是string 格式,在注入是如果不手动转换那么将无法注入-->
   <bean factory-bean="dateFormat" factory-method="parse">
      <constructor-arg value="2007-12-20"></constructor-arg>
   </bean>
</constructor-arg>

G、解析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);
      }
   }
}

parsePropertyElement函数如下:

public void parsePropertyElement(Element ele, BeanDefinition bd) {
   //获取name
   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;
      }
      //复用构造参数解析的parsePropertyValue函数
      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标签的解析是和构造函数的子标签解析是一样的,复用parsePropertyValue方法。

spring默认标签解析

原文:https://www.cnblogs.com/wangerxiaoblog/p/12336651.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!