Adolph
Engineer
Engineer
  • UID623
  • Fans2
  • Follows1
  • Posts72
Reads:1257Replies:0

Spring source code profiling - Bean configuration and startup (3)

Created#
More Posted time:Sep 27, 2016 14:00 PM
Get BeanDefinition object
 Now we have got the Document object after XML parsing. Next we only need to analyze the Document structure to know how beans are defined in XML and how to convert beans to BeanDefinition objects.
 This process calls the registerBeanDefinitions(Document doc, Resource resource) method of XmlBeanDefinitionReader class, and the code is as below:
XmlBeanDefinitionReader class
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
  
        // The specific parsing process is completed in the registerBeanDefinitions method of BeanDefinitionDocumentReader.
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;    
}
  
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));    
}
DefaultBeanDefinitionDocumentReader class
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
  
public static final String NESTED_BEANS_ELEMENT = "beans";
  
public static final String ALIAS_ELEMENT = "alias";
  
public static final String ALIAS_ATTRIBUTE = "alias";
  
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();    // Get the root element of Document
        doRegisterBeanDefinitions(root);
}
  
protected void doRegisterBeanDefinitions(Element root) {
  
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
  
        if (this.delegate.isDefaultNamespace(root)) {
                String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                if (StringUtils.hasText(profileSpec)) {
                        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                                profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                        if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                                return;
                        }
                }
        }
  
        preProcessXml(root);
  
        //From here, we can see that XmlBeanDefinitionReader parses specific element attributes by calling parseBeanDefinitions.
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);
  
        this.delegate = parent;
}
  
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);    // Parse self-defined root node of the element  
        }
}
  
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {    // Parse import element
                importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {    // Parse alias element
                processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {    // Parse bean element
                processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {    // Parse nested beans element as root nodes for recursive parsing
                doRegisterBeanDefinitions(ele);
        }
}
  
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // The specific parsing is entrusted to BeanDefinitionParserDelegate to complete  
        // BeanDefinitionHolder is the encapsulation class of BeanDefinition. It encapsulates BeanDefinition, bean name and alias and it is used to complete registration to IOC containers.
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try {
                        BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                }
                catch (BeanDefinitionStoreException ex) {
                        getReaderContext().error("Failed to register bean definition with name '" +
                                bdHolder.getBeanName() + "'", ele, ex);
                }
                getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
}
From the code above, we can see that the final parsing task is handed over to the processBeanDefinition method. The implementation process of this method is in the BeanDefinitionParserDelegate class.
BeanDefinitionParserDelegate class
public class BeanDefinitionParserDelegate {
  
    public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
  
    public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
  
    /**
     * Value of a T/F attribute that represents true.
     * Anything else represents false. Case seNsItive.
     */
    public static final String TRUE_VALUE = "true";
  
    public static final String FALSE_VALUE = "false";
  
    public static final String DEFAULT_VALUE = "default";
  
    public static final String DESCRIPTION_ELEMENT = "description";
  
    public static final String AUTOWIRE_NO_VALUE = "no";
  
    public static final String AUTOWIRE_BY_NAME_VALUE = "byName";
  
    public static final String AUTOWIRE_BY_TYPE_VALUE = "byType";
  
    public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
  
    public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
  
    public static final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all";
  
    public static final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple";
  
    public static final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects";
  
    public static final String NAME_ATTRIBUTE = "name";
  
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
}
  
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        // Parse the bean to define <Bean> elements in resource files, mainly processing the id, name and alias attributes of <Bean> elements.
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_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 (!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);
        }
  
        // Parse <bean> elements in detail
        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);
                                        }
                                }
                                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;
}
  
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);
                }
                // Create BeanDefinition based on the class name and parent attributes configured by the <Bean> elements and prepare for loading bean definition information  
                AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  
  
                parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
                bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  
                // Parse sub-elements of <Bean> elements such as meta, lookup-method and replaced-method.  
                parseMetaElements(ele, bd);
                parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
                parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  
                parseConstructorArgElements(ele, bd);    // Parse the constructor parameters of <Bean> elements  
                parsePropertyElements(ele, bd);    // Parse the <property> attribute of <Bean> elements 
                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;
}
}
I will not analyze in detail the above process. If you are interested, you can read articles about implementation of BeanDefinitionParserDelegate. Its main logic is to call different processing procedures according to different tags to complete parsing BeanDefinition. Once this step is done, we are not far from achieving our goal.
BeanDefinition registration
 The bean information we configured has been converted to the uniform structure of BeanDefinition after parsing in Spring, but the data cannot be used by the IOC container directly. We need to register the BeanDefinition data in IOC container.
 For registration, the registerBeanDefinition method of DefaultListableBeanFactory is called.
DefaultListableBeanFactory class

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
  
     //Registered BeanDefinition will be handed over to Map for management with the beanName as the key and the beanDefinition as the value.
    /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
  
    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");
  
        // Validate the BeanDefinition after parsing
        if (beanDefinition instanceof AbstractBeanDefinition) {
                try {
                        ((AbstractBeanDefinition) beanDefinition).validate();
                }
                catch (BeanDefinitionValidationException ex) {
                        throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                "Validation of bean definition failed", ex);
                }
        }
  
        BeanDefinition oldBeanDefinition;
  
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
                if (!isAllowBeanDefinitionOverriding()) {
                        throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                                "': There is already [" + oldBeanDefinition + "] bound.");
                }
                else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                        if (this.logger.isWarnEnabled()) {
                                this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                                        "' with a framework-generated bean definition: replacing [" +
                                        oldBeanDefinition + "] with [" + beanDefinition + "]");
                        }
                }
                else if (!beanDefinition.equals(oldBeanDefinition)) {
                        if (this.logger.isInfoEnabled()) {
                                this.logger.info("Overriding bean definition for bean '" + beanName +
                                        "' with a different definition: replacing [" + oldBeanDefinition +
                                        "] with [" + beanDefinition + "]");
                        }
                }
                else {
                        if (this.logger.isDebugEnabled()) {
                                this.logger.debug("Overriding bean definition for bean '" + beanName +
                                        "' with an equivalent definition: replacing [" + oldBeanDefinition +
                                        "] with [" + beanDefinition + "]");
                        }
                }
                this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
  
                if (hasBeanCreationStarted()) {
  
                        synchronized (this.beanDefinitionMap) {
                                this.beanDefinitionMap.put(beanName, beanDefinition);
                                List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                                updatedDefinitions.addAll(this.beanDefinitionNames);
                                updatedDefinitions.add(beanName);
                                this.beanDefinitionNames = updatedDefinitions;
                                if (this.manualSingletonNames.contains(beanName)) {
                                        Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                                        updatedSingletons.remove(beanName);
                                        this.manualSingletonNames = updatedSingletons;
                                }
                        }
                }
                else {
  
                        this.beanDefinitionMap.put(beanName, beanDefinition);
                        this.beanDefinitionNames.add(beanName);
                        this.manualSingletonNames.remove(beanName);
                }
                this.frozenBeanDefinitionNames = null;
        }
  
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
                resetBeanDefinition(beanName);
        }
}
  
  
}
Concluding remarks
 After the above registration operation is complete, a simple IOC container is ready for use, putting an end to our analysis today. It is indeed complicatedbut please do not be overwhelmed. (>_<)You can never understand the internal structure of Spring overnight. The best way to learn it is to read articles to get a rough idea about its procedures and then follow up and study it on your own.  I am also new to Spring source code. I look forward to your feedback if there are any deficiencies in my writings.
Guest