2. Resolution Default Label of Spring Source Analysis 2

1. Resolving default labels

parseDefaultElement(ele, delegate);

void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   //Parsing import Tags
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   //Parsing alias Tags
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
    //Resolving bean Tags
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   //Parsing beans tags, recursive processing
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      doRegisterBeanDefinitions(ele);
   }
}

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //1. Delegate BeanDefinitionPaserDelegate class for element parsing, encapsulate BeanDefinition into BeanDefinitionHolder
   //Contains class, name, id attributes
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

   if (bdHolder != null) {
      //2. If there are custom attributes under the child nodes of the default label, it is necessary to parse the custom label again.
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         //3. Delegate Bean Definition Reader Utils registration
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,  getReaderContext().getRegistry());
      }
      //4. Send a response event to notify the listening event you want to care about
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

2. Parsing Bean Definition

BeanDefinition is an interface whose Abstract implementation class is AbstractBeanDefinition and has three subclasses: RootBeanDefinition and ChildBeanDefinition.
GenericBeanDefinition. BeanDefinition is the internal representation of the configuration file bean element in the container, corresponding to the attributes in the bean. Spring passes through
 BeanDefinition converts the bean configuration information in the configuration file into the internal representation of the container and registers this information in the BeanDefinition Registry, which is Spring.
The memory database of configuration information is a Map, and subsequent operations read configuration information directly from the Bean Definition Registry.

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
   //Parsing id attributes
   String id = ele.getAttribute(ID_ATTRIBUTE);
   //Resolve the name attribute
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
   
   //If nameAttr is not empty, parse the alias attribute
   List<String> aliases = new ArrayList<String>();
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }

   String beanName = id;
   //If no id attribute is specified, use the first of the aliases instead
   if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      beanName = aliases.remove(0);
   }

   if (containingBean == null) {
      checkNameUniqueness(beanName, aliases, ele);
   }
   
   //Further parse all other attributes and encapsulate them in bean Definition
   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);
 
               String beanClassName = beanDefinition.getBeanClassName();
               if (beanClassName != null &&
                     beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                     !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                  aliases.add(beanClassName);
               }
            }
         }
      }
      String[] aliasesArray = StringUtils.toStringArray(aliases);
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }

   return null;
}
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {

   String className = null;
   //Resolving className attributes
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }
   try {
      String parent = null;
      //Parsing parent attributes
      if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
         parent = ele.getAttribute(PARENT_ATTRIBUTE);
      }

      //Create GenericBeanDefinition for carrying attributes, directly new, and set the parameters passed in
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);

      //1. Hard-coded parsing bean properties
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

      //2. Analytical description
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

      //3. Analyzing metadata
      parseMetaElements(ele, bd);

      //4. To parse lookup-method properties, see Appendix 1. The actual bean s to be returned are configured in the configuration file.
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

      //5. Resolving the replace-method attribute can not only dynamically replace the bean s returning entities, but also dynamically change the logic of the original method.
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

      //6. Analytic constructor parameters, property elements and qualifier elements
      parseConstructorArgElements(ele, bd);
      parsePropertyElements(ele, bd);
      parseQualifierElements(ele, bd);

      bd.setResource(this.readerContext.getResource());
      bd.setSource(extractSource(ele));

      return bd;
   }
}
Step 1: Hard-coded parsing of various properties of bean s
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String 
                                                    beanName,BeanDefinition containingBean, AbstractBeanDefinition bd) {
   //Resolving scope attributes
   if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
      bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
      if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
         error("Specify either 'scope' or 'singleton', not both", ele);
      }
   }
   //Resolving singleton attributes
   else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
      bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
                                                       BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
   }
   else if (containingBean != null) {
      bd.setScope(containingBean.getScope());
   }

   if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
      bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
   }
   
   //Resolve the lazy-init property, if not set or set to other characters, it will be set to false
   String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
   if (DEFAULT_VALUE.equals(lazyInit)) {
      lazyInit = this.defaults.getLazyInit();
   }
   bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

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

   String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
   bd.setDependencyCheck(getDependencyCheck(dependencyCheck));

   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 ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(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)));
   }
   
   //Resolving init-method attributes
   if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
      String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
      if (!"".equals(initMethodName)) {
         bd.setInitMethodName(initMethodName);
      }
   }
   else {
      if (this.defaults.getInitMethod() != null) {
         bd.setInitMethodName(this.defaults.getInitMethod());
         bd.setEnforceInitMethod(false);
      }
   }
   
   //Resolving destroy-method attributes
   if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
      String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
      if (!"".equals(destroyMethodName)) {
         bd.setDestroyMethodName(destroyMethodName);
      }
   }
   else {
      if (this.defaults.getDestroyMethod() != null) {
         bd.setDestroyMethodName(this.defaults.getDestroyMethod());
         bd.setEnforceDestroyMethod(false);
      }
   }
   //Resolving factory-method attributes
   if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
      bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
   }
   //Resolving factory-bean attributes
   if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
      bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
   }

   return bd;
}
Steps 4 and 5: See Appendixes 1 and 2

Step 6: Analytical constructor parameters

3,AbstractBeanDefinition

The parsed data are placed in AbstractBean Definition.
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable {

   private volatile Object beanClass;

   private String scope = SCOPE_DEFAULT;

   private boolean singleton = true;

   private boolean prototype = false;

   private boolean abstractFlag = false;

   private boolean lazyInit = false;

   private int autowireMode = AUTOWIRE_NO;

   private int dependencyCheck = DEPENDENCY_CHECK_NONE;

   private String[] dependsOn;
   //Constructor properties
   private ConstructorArgumentValues constructorArgumentValues;
   //General Attribute Set
   private MutablePropertyValues propertyValues;
   //Holder of method rewriting, recording lookup-method, replace-method elements
   private MethodOverrides methodOverrides = new MethodOverrides();

   private String factoryBeanName;

   private String factoryMethodName;

   private String initMethodName;

   private String destroyMethodName;
   //Whether to execute init-method and destroy-method
   private boolean enforceInitMethod = true;

   private boolean enforceDestroyMethod = true;
}

4. Register BeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //Parsing BeanDefinition
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         //Register BeanDefinition to registry
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      //Notify the listener to parse and complete the registration. When the programmer needs to monitor the registered BeanDefinition event, he can register the listener's party.
      //And write the processing logic to the listener. At present, Spring does not do any logical processing for this event.
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry){
   
String beanName = definitionHolder.getBeanName();
   //Use beanName for Unique Identity Registration
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String aliase : aliases) {
         //Use aliases for registration
         registry.registerAlias(beanName, aliase);
      }
   }
}
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {

   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         //Check that the exception part has been deleted
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
   }

   //Bean Definition Map is a global variable that stores registration information. It has concurrent access and locks. It is Concurrent HashMap.
   //Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
   synchronized (this.beanDefinitionMap) {
      Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
      //Dealing with registered beanName
      if (oldBeanDefinition != null) {
         if (!this.allowBeanDefinitionOverriding) {
             //The code here is brief, throwing exceptions if coverage is not allowed, and logging if coverage is allowed.
         }
      }
      else {
         this.beanDefinitionNames.add(beanName);
         this.frozenBeanDefinitionNames = null;
      }
      //Real registration
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   //Clear cache
   resetBeanDefinition(beanName);
}

5. Resolving alias Tags

protected void processAliasRegistration(Element ele) {
   //Get the bean Name
   String name = ele.getAttribute(NAME_ATTRIBUTE);
   //Get alias
   String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
   boolean valid = true;
   //Previously, some checks were deleted.
   if (valid) {
      try {
         //register
         getReaderContext().getRegistry().registerAlias(name, alias);
      }
      getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
   }
}

6. Resolving import Tags

protected void importBeanDefinitionResource(Element ele) {
   //Resolve the resource attribute, such as <import resource="***.xml"/>
   String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
   //There is no direct return
   if (!StringUtils.hasText(location)) {
      getReaderContext().error("Resource location must not be empty", ele);
      return;
   }

   location = environment.resolveRequiredPlaceholders(location);
   Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
   //Judging whether it is a relative or absolute path
   boolean absoluteLocation = false;
   try {
      absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
   }
 
   if (absoluteLocation) {
      try {
         //Is the absolute path, directly calling loadBeanDefinitions to load BeanDefinition, recursive operation
         int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
      }
   }else {
      try {
         int importCount;
         //Calculate the absolute path and load Bean Definitons
         Resource relativeResource = getReaderContext().getResource().createRelative(location);
         if (relativeResource.exists()) {
            importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
            actualResources.add(relativeResource);
         }
         else {
            String baseLocation = getReaderContext().getResource().getURL().toString();
            importCount = getReaderContext().getReader().loadBeanDefinitions(
                  StringUtils.applyRelativePath(baseLocation, location), actualResources);
         }
      }
 }
   Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
   getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
Appendix 1: Analyzing lookup-method
public class User {
    public void showMe(){
        System.out.println("I am a User");
    }
}

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

public abstract class GetBeanTest {

    public void showMe(){
        this.getBean().showMe();
    }

    public abstract User getBean();
}
<bean id="getBeanTest" class="com.lwh.spring.look.GetBeanTest">
    <lookup-method name="getBean" bean="teacher"/>
</bean>

<bean id="teacher" class="com.lwh.spring.look.Teacher"/>
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;
         //Get the method to decorate
         String methodName = ele.getAttribute(NAME_ATTRIBUTE);
         //Get the bean returned from the configuration
         String beanRef = ele.getAttribute(BEAN_ELEMENT);
         LookupOverride override = new LookupOverride(methodName, beanRef);
         override.setSource(extractSource(ele));
         overrides.addOverride(override);
      }
   }
}

Appendix 2: Analyzing replace-method 
public class TestChangeMethod {
    public void changeMe(){
        System.out.println("changeMe");
    }
}

public class TestMethodReplacer implements MethodReplacer{

    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("I replaced the original method.");
        return null;
    }
}
<bean id="testChangeMethod" class="com.lwh.spring.replace.TestChangeMethod">
    <replaced-method name="changeMe" replacer="replacer"/>
</bean>

<bean id="replacer" class="com.lwh.spring.replace.TestMethodReplacer"/>
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      //Valid only if it is under the child element of Spring default bean and is replace-method
      if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
         Element replacedMethodEle = (Element) node;
         //Extract the old method to replace, changeMe
         String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
         //Extract the corresponding new replacement method, replacer
         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) {
            //Recording parameters
            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);
      }
   }
}

Tags: Programming Spring Attribute Database xml

Posted on Thu, 10 Oct 2019 12:40:09 -0700 by dflow