In the last article “Spring source analysis (a) understand the Spring container refresh logic” we understand the Spring container refresh logic, this article we on the inside of the BeanFactory creation and XML configuration parsing good analysis.

Simple example

We use the simplest example to explore the entry point of source code entry.

First we configure the bean parameters in the XML configuration file:

<bean id="user" class="com.mrlsm.spring.demo.entity.User" scope="singleton" lazy-init="true" primary="true"/>
Copy the code

Corresponding entity class User

public class User {}
Copy the code

Test main method:

ApplicationContext context2 = new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User) context2.getBean("user");
System.out.println(user);
Copy the code

Output result:Ok, the instance runs successfully and we start analyzing:

Source code analysis

A, refresh

ClassPathXmlApplicationContext create contxt objects, we found will enter AbstractApplicationContext# refresh () method.

There is a lot of processing in the refresh method, and this section only examines the creation of the BeanFactory.

@Override
public void refresh(a) throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for the refresh. Updates to status values
		prepareRefresh();
		// Tells subclasses to refresh the internal bean factory. This section only analyzes the creation of the beanFactory
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// Register some system beans needed in the container such as: classLoader, etc
		prepareBeanFactory(beanFactory);
        
		// special processing for beanFactory}}Copy the code

Debug Indicates the debugging

The execution of the AbstractApplicationContext * * # * * obtainFreshBeanFactory () method after the container has been BeanDefinition containing the user. So let’s focus on what the obtainFreshBeanFactory method does.

Second, the refreshBeanFactory

At this point: to locate AbstractRefreshableApplicationContext * * # * * refreshBeanFactory ()

@Override
protected final void refreshBeanFactory(a) throws BeansException {
    // Check whether the beanFactory is already held and whether the beanFactory is refreshed
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
        Create a new beanFactory
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
        // Customize the internal bean factory used by this context.
		customizeBeanFactory(beanFactory);
        // Load all BeanDefinitions to actually parse the XML
		loadBeanDefinitions(beanFactory);
		this.beanFactory = beanFactory;
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code

Third, create DefaultListableBeanFactory

Create a DefaultListableBeanFactory (spring of engine, operating in the form of collection list beans)

The class diagram is as follows: a key class in Spring, and its member methods are analyzed in detail here

Four, loadBeanDefinitions

AbstractRefreshableApplicationContext#loadBeanDefinitions()

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	// Delegate mode is used here, delegating BeanDefinitionReader to BeanDefinitionReader
	// Delegate to XmlBeanDefinitionReader since we are currently parsing XML.
	/ / will be entrusted to AnnotatedBeanDefinitionReader use annotations way
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	
        // Set some attributes for BeanDefinitionReader
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	
        // Allow subclasses to provide a custom initialization of the reader and then proceed with the actual loading of the bean definition.
	initBeanDefinitionReader(beanDefinitionReader);
	loadBeanDefinitions(beanDefinitionReader);
}
Copy the code

Let’s move on:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	Resource[] configResources = getConfigResources();
	if(configResources ! =null) {
		reader.loadBeanDefinitions(configResources);
	}
	/ / configuration information position: new ClassPathXmlApplicationContext (" XXX ") of the incoming
	String[] configLocations = getConfigLocations();
	if(configLocations ! =null) {
		// Delegate to Reader to load BeanDefinitionreader.loadBeanDefinitions(configLocations); }}Copy the code

Loads the bean definition from the specified XML file.

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	//... prejudgment
	try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
		InputSource inputSource = new InputSource(inputStream);
		if(encodedResource.getEncoding() ! =null) {
			inputSource.setEncoding(encodedResource.getEncoding());
		}
		return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	}
	// exception handling
}
Copy the code

Fifth, doLoadBeanDefinitions

Actually loads the bean definition from the specified XML file.

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
        // Input stream information for the configuration file is loaded as a Document object
		Document doc = doLoadDocument(inputSource, resource);
		int count = registerBeanDefinitions(doc, resource);
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}
	// exception handling
}
Copy the code

Six, registerBeanDefinitions

Parse and register BeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    / / the Document parsing is delegated to the BeanDefinitionDocumentReader (for read bean definitions from an XML Document).
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    // Delegate the documentReader to parse the registered BeanDefinition. Note that an XmlReaderContext is passed in
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    // Returns the number of bean definitions found
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

// You can see that the constructor of XmlReaderContext passes the current class XmlBeanDefinitionReader
// The current class holds BeanDefinitionRegistry, so XmlReaderContext holds a BeanDefinitionRegistry
public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                                this.sourceExtractor, this, getNamespaceHandlerResolver());
}
Copy the code

Seven, doRegisterBeanDefinitions

Follow up with the delegate documentReader resolution register BeanDefinition

protected void doRegisterBeanDefinitions(Element root) {
	// Create an associated proxy
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(getReaderContext(), root, parent); ...// The empty implementation allows XML to be extensible
	preProcessXml(root);
    // Parse the document for root level elements: "import", "alias", "bean".
	parseBeanDefinitions(root, this.delegate);
    // The empty implementation allows XML to be extensible
	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)) {
					// Parse the default tags
					parseDefaultElement(ele, delegate);
				}
				else {
					// Parse custom tagsdelegate.parseCustomElement(ele); }}}}else {
		// Parse custom tagsdelegate.parseCustomElement(root); }}Copy the code

Eight, parseDefaultElement

We will only look at default tag parsing here

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    // Parsing the import tag, recursive parsing the imported XML,
    // See what the import tag can do to import other configuration files
    // It is not hard to see that it is used to load the bean definition from a given resource into the bean factory.
    // The main method is loadBeanDefinitions
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    // Parse the alias tag
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    // Parse the bean label
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    // Parse the beans label
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // Recurse BeanDefinitions againdoRegisterBeanDefinitions(ele); }}Copy the code

Nine, processBeanDefinition

Locate the resolution of the bean label

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // The bean label will be parsed and encapsulated in the BeanDefinition
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if(bdHolder ! =null) {
        // Decorate the BeanDefinition parsed out of the bean label
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            / / register BeanDefinition
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        } 
        catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
		}
		// Send the registration event
		getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder)); }}Copy the code

Ten, parseBeanDefinitionElement

Continue to follow up parseBeanDefinitionElement method

@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));
	}
	// beanName is an ID by default. If no ID is specified, the first name will be used as beanName
	String beanName = id;
	if(! StringUtils.hasText(beanName) && ! aliases.isEmpty()) { beanName = aliases.remove(0);
		if (logger.isTraceEnabled()) {
			logger.trace("No XML 'id' specified - using '" + beanName +
					"' as bean name and " + aliases + " as aliases"); }}if (containingBean == null) {
		// Check whether the beanName and alias are the same
		checkNameUniqueness(beanName, aliases, ele);
	}
	// Parse the XML to get a beanDefinition
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if(beanDefinition ! =null) {
		if(! StringUtils.hasText(beanName)) {// Generate a beanName
		}
		String[] aliasesArray = StringUtils.toStringArray(aliases);
		// Encapsulate beanDefinition as holder
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}
	return null;
}
Copy the code

Then we follow up with parsing XML tag attributes

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 {
		// Create a GenericBeanDefinition
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);
		// Parse the properties scope, abstract, lazy-init, and so on on the bean label and set the values to BeanDefinition
		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
		// Set the description property
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
        
		// Parse meta subtags
		parseMetaElements(ele, bd);
		// parse the subtag of lookup-method
		parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
		// Resolve the appet-method subtag
		parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
		// Parse the constructor-arg subtag
		parseConstructorArgElements(ele, bd);
		// Parse the property subtag
		parsePropertyElements(ele, bd);
		// Resolve the qualifier sub-tag
		parseQualifierElements(ele, bd);

		bd.setResource(this.readerContext.getResource());
		bd.setSource(extractSource(ele));
		return bd;
	}
	// ··· catch
	return null;
}
Copy the code

These parsing methods are easy to see:

The AbstractBeanDefinition method implements a loop to find the tag and set it to AbstractBeanDefinition.

At this point, a default bean label is parsed and all the information it contains is encapsulated in a BeanDefinition instance, which will then be registered with our IOC container in preparation for the next instance generation.

Eleven, BeanDefinitionReaderUtils# registerBeanDefinition ()

Going back to processBeanDefinition at point 9, we see that we end up putting the instances generated in the previous step into beanDefinitionMap and beanDefinitionNames.

While BeanDefinitionReaderUtils# registerBeanDefinition () method would be BeanDefinition registered parsing with the Spring IOC container, This is the entry that BeanDefinition registers with the Spring IOC container.

public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    throws BeanDefinitionStoreException {
    String beanName = definitionHolder.getBeanName();
    // The main implementation
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    String[] aliases = definitionHolder.getAliases();
    if(aliases ! =null) {
        for (String alias : aliases) {
            // Register an aliasregistry.registerAlias(beanName, alias); }}}// DefaultListableBeanFactory#registerBeanDefinition()
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {
	/ /... There is a lot of branch judgment and exception handling ahead
    
    // Place the current beanDefinition into the following two containers
    this.beanDefinitionMap.put(beanName, beanDefinition);
    this.beanDefinitionNames.add(beanName); 
    // Update the manualSingletonNames container
    removeManualSingletonName(beanName);
}
Copy the code

summary

From the above, we know that the parsing and processing of spring’s default tag bean is to obtain various attributes configured in the bean tag in XML and encapsulate them into a BeanDefinition object. Then store this object in our IOC container beanDefinitionMap, beanDefinitionNames, for future use and retrieval in the IOC container. The ConfigurableListableBeanFactory is created.

This article is based on the Spring-Framework 5.2.10.release