Previous posts:

1. Deep understanding of Spring IOC(I), unified resource loading

In the last article, we looked at Spring’s uniform Resource loading strategy, and we learned that XML must be loaded as Resource using ResourceLoader before it can be used. However, ResourceLoader can only load one file as Resource at a time, so when you need to load multiple resources at the same time, you need to use ResourcePatternReslover. So what happens next, after you load the XML as a Resource, what happens to the information in the XML after you parse it? This article will answer that question.

BeanDefinition

This is one thing we have to say before we talk about parsing XML. Let’s look at the description of this interface in the source code:

Why do you have to say this? Because BeanDefinition is the basis for you to really read the source code, a lot of the extensions are based on it, and a lot of the core flow requires you to understand it. Many people claim to have read the Spring source code, but end up not knowing even the most basic BeanDefinition.Copy the code

BeanDefinition describes the attributes of an instance of a bean, its constructor parameters, and more information supported by subclasses. Let’s look at the inheritance of the BeanDefinition interface:

Priority is AbstractBeanDefinition this abstract class, it has a default implementation for most BeanDefinition method, and a lot of properties, these properties determine the Spring how to instantiate the corresponding bean. AbstractBeanDefinition class AbstractBeanDefinition class AbstractBeanDefinition class AbstractBeanDefinition class AbstractBeanDefinition

There are a lot of attributes. You don't need to remember them. You can read most of them firstCopy the code
The code block1
        // The Java class corresponding to the bean
	@Nullable
	private volatile Object beanClass;
	// The scope of the bean, corresponding to the scope property
	@Nullable
	private String scope = SCOPE_DEFAULT;
	// Whether the class is abstract
	private boolean abstractFlag = false;
	// Whether to load lazily
	private boolean lazyInit = false;
	// Automatic injection
	private int autowireMode = AUTOWIRE_NO;
	// Dependency check
	private int dependencyCheck = DEPENDENCY_CHECK_NONE;
	DependenOn specifies that an instantiation of a bean depends on another bean being instantiated first. This attribute is dependent on the @dependenon attribute
	@Nullable
	private String[] dependsOn;
	// The autowire-candidate property is set to false so that the bean is not considered by the container when it looks for an autowier-candidate, that is, it is not considered as a candidate for other bean autowier-candidate,
        // However, the bean itself can be used to inject other beans using autowiring
	private boolean autowireCandidate = true;
	// When multiple bean candidates appear during autowiring, they will be preferred, corresponding to the bean property primary, with the @primary annotation
	private boolean primary = false;
	// Records the Qualifier, corresponding to the child element Qualifier
	private final Map<String, AutowireCandidateQualifier> qualifiers = newLinkedHashMap<>(); @Nullable private Supplier<? > instanceSupplier; private boolean nonPublicAccessAllowed =true;
	// Whether to parse the constructor in a loose mode. Default is true
	// If you don't understand, skip ahead. We will cover this later
	private boolean lenientConstructorResolution = true;
	// Corresponds to the bean label attribute factory-bean
	@Nullable
	private String factoryBeanName;
	// Corresponds to the bean label attribute factory-method
	@Nullable
	private String factoryMethodName;
	// Record the constructor injection property corresponding to the bean property constructor-arg
	@Nullable
	private ConstructorArgumentValues constructorArgumentValues;
	// Set of common attributes
	@Nullable
	private MutablePropertyValues propertyValues;
	// The holder of the method override, recording the lookup-method and appet-method elements
	@Nullable
	private MethodOverrides methodOverrides;
	// Initialize the method corresponding to the bean property init-method
	@Nullable
	private String initMethodName;
	// Destroy method corresponding to bean property destroy-method
	@Nullable
	private String destroyMethodName;
	// Whether to execute init-method, program Settings
	private boolean enforceInitMethod = true;
	// Whether to destroy-method, program Settings
	private boolean enforceDestroyMethod = true;
	// If AOP is defined by the user rather than the application itself, set to true when AOP is created
	private boolean synthetic = false;
	// Define the APPLICATION of this bean, APPLICATION: user, INFRASTRUCTURE: completely internal use, independent of user, SUPPORT: part of some complex configuration
        // Program Settings
	private int role = BeanDefinition.ROLE_APPLICATION;
	// Describes the bean
	@Nullable
	private String description;
	// Resource defined by this bean
	@Nullable
	private Resource resource;
Copy the code

I’ve just listed most of the attributes for the sake of less code. As you can see, there are a lot of attributes, we can almost look at XML and see what it does, and there are a lot of attributes that we’re not familiar with, so don’t worry, just take a look at them, get familiar with them, and I’ll come back to them later if we need to. It is not necessary to remember all of the above attributes, but to remember that the contents of each bean tag in XML will eventually be parsed into a subclass of this BeanDefinition.

BeanDefinitionReader

BeanDefinitionReader (BeanDefinitionReader) {BeanDefinitionReader (BeanDefinitionReader);

The code block2
public interface BeanDefinitionReader {

	BeanDefinitionRegistry getRegistry();

	ResourceLoader getResourceLoader();

	ClassLoader getBeanClassLoader();

	BeanNameGenerator getBeanNameGenerator();

        // Core four methods
        
	int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

	int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

	int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

	int loadBeanDefinitions(String. locations) throws BeanDefinitionStoreException; }Copy the code

The four loadBeanDefinitions methods take different parameters to load a resource, and then return the number of loaded resources. So it’s also easy to understand that the core role of the implementation class of this interface is to load and parse beanDefinitions based on the resource or location of the resource. Let’s look at the inheritance of this interface:

We don’t pay attention to EnvironmentCapable interface first, then looked down from the top, had seen the AbstractBeanDefinitionReader first, and other system routines are similar, this class provides BeanDefinitionReader many default implementation method, Let’s look at its implementation of loadBeanDefinitions:

The code block3
        @Override
	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int counter = 0;
		for (Resource resource : resources) {
		        // An unimplemented method was called
			counter += loadBeanDefinitions(resource);
		}
		return counter;
	}
	
	@Override
	public int loadBeanDefinitions(String. locations) throws BeanDefinitionStoreException { Assert.notNull(locations,"Location array must not be null");
		int counter = 0;
		for (String location : locations) {
		        // The following method is called
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}

	@Override
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
	        // The following method is called
		return loadBeanDefinitions(location, null);
	}
        // This is an overloaded method
	public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}
		If else, use ResourcePatternResolver if the class's resourceLoader attribute is a ResourcePatternResolver instance
		// Load resources in batches and then parse them, otherwise load individual resources with normal ResourceLoader
		if (resourceLoader instanceof ResourcePatternResolver) {
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				int loadCount = loadBeanDefinitions(resources);
				if(actualResources ! =null) {
					for(Resource resource : resources) { actualResources.add(resource); }}if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex); }}else {
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			if(actualResources ! =null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			returnloadCount; }}Copy the code

Don’t be scared by such a long code, this code belongs to the paper tiger, it is easy to understand carefully. We can easily see that AbstractBeanDefinitionReader does not implement the loadBeanDefinitions BeanDefinitionReader (the Resource the Resource) this method, Instead, it implemented three other methods and added an overloaded method. However, all other implementations end up calling the unimplemented method (and the overloaded method is also called), so we can see that parsing a specific resource, Again, we have to use loadBeanDefinitions (Resource Resource), You need to see us this key is a subclass of AbstractBeanDefinitionReader XmlBeanDefinitionReader (as for why choose this, because we are so far, that’s all is based on the XML configuration). For XmlBeanDefinitionReader, see loadBeanDefinitions (Resource Resource).

The code block4
        @Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}
Copy the code

LoadBeanDefinitions (EncodeResource EncodeResource) loadBeanDefinitions (EncodeResource EncodeResource)

The code block5
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
                // The parameter cannot be null validation
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}
		
		// 1. Check whether one XML and another XML refer to each other through import tags
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		// 2
		// 1 = 1; // 2 = 1; // 2 = 2
		if(! currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		
		// The key is to translate Resource into InputSource and deliver it to the real doLoadBeanDefinitions.
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if(encodedResource.getEncoding() ! =null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally{ inputStream.close(); }}catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove(); }}}Copy the code

The most important thing in the code above, which is actually step 3, is to look at the doLoadBeanDefinitions method:

The code block6
        protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			// Get the Document object (Document stands for HTML or XML). At this stage, Spring doesn't know that your Resource is XML
			Document doc = doLoadDocument(inputSource, resource);
			// Parse XML to register BeanDefinitions
			return registerBeanDefinitions(doc, resource);
		}
		catch (BeanDefinitionStoreException ex) {
			throwex; }...Copy the code

The definitions of doLoadBeanDefinitions are defined by the definitions of doLoadDocument (see definitions of doLoadBeanDefinitions)

The code block7
        // I put the documentLoader in this location
        private DocumentLoader documentLoader = new DefaultDocumentLoader();
	
	protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
	        / / load the Document
		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
				getValidationModeForResource(resource), isNamespaceAware());
	}
Copy the code

As you can see from the above code, the documentLoader is an instance of DefaultDocumentLoader (I specifically put the member attribute in this location). In this loadDocument method, the most important parameter is the fourth parameter. This parameter is obtained by getValidationModeForResource, we first look at this method:

The code block8
        protected int getValidationModeForResource(Resource resource) {
		/ / to have set a good validation model, in the case of ClassPathXmlApplicationoContext, this value
		// The value of validationModeToUse is 1 if you debug the code from the previous article
		int validationModeToUse = getValidationMode();
		// VALIDATION_AUTO is 1, so don't go if
		// (VALIDATION_AUTO represents a way for Spring to discover what validation_resources should be used)
		if(validationModeToUse ! = VALIDATION_AUTO) {return validationModeToUse;
		}
		DetectedMode returned 3 after the debug code started in the previous article
		// If we were using a DTD format file, 2 would be returned
		int detectedMode = detectValidationMode(resource);
		if(detectedMode ! = VALIDATION_AUTO) {return detectedMode;
		}
		// Return XSD verification mode, which is also 3
		return VALIDATION_XSD;
	}
Copy the code

Then the previous loadDocument method takes this parameter and parses the inputSource of the first parameter into an XML Document object based on this parameter. For space, I won’t put the code for this method here. Then look at the registerBeanDefinitions(Document Doc, Resource Resource) method in XmlBeanDefinitionReader, which is essentially the second line from block 6:

The code block9
        @SuppressWarnings("deprecation")
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		/ / 1. Use reflection to create DefaultBeanDefinitionDocumentReader instance objects
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		// 2. Set the environment
		documentReader.setEnvironment(getEnvironment());
		// 3. Get the number of registered BeanDefinitions
		int countBefore = getRegistry().getBeanDefinitionCount();
		// 4. Actually register BeanDefinition, the actual process of parsing XML
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		// Returns the number of registered BeanDefinitions
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
Copy the code

The focus is on the fourth line of code, we can see that real parsing XML or to BeanDefinitionDocumentReader registerBeanDefinitions method to do this class, this method has two parameters, The first is the XML Document object that we converted earlier. The second is actually an XmlReaderContext (which we’ll leave out in extensions). Let’s look at the registerBeanDefinitions method:

The code block10
        @Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		/ / is to take all the root node and then handed over to the real doRegisterBeanDefinitions to parsing
		// There are a lot of similar styles in spring source code, but doXXX is where the real work is...
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}
Copy the code

Then look at real work doRegisterBeanDefinitions method

The code block11
        protected void doRegisterBeanDefinitions(Element root) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		// This if block deals with profiles (ignore)
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			if(! getEnvironment().acceptsProfiles(specifiedProfiles)) {return; }}// Any nested <beans> elements will cause recursion in this method. In
		// order to propagate and preserve <beans> default-* attributes correctly,
		// keep track of the current (parent) delegate, which may be null. Create
		// the new (child) delegate with a reference to the parent for fallback purposes,
		// then ultimately reset this.delegate back to its original (parent) reference.
		// this behavior emulates a stack of delegates without actually necessitating one.
		// Since beans tags can be nested within beans tags, recursive calls to root are different, so we need to create a new delegate
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(this.readerContext, root, parent);

		preProcessXml(root);
		// Start from root, (core logic)
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}
Copy the code

See parseBeanDefinitions of core logic

The code block12
        protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		/ / is looking at the tag to have "http://www.springframework.org/schema/beans", if you have, that is not the developers to define their own XML, if you go
		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 tags (more on custom tags later)delegate.parseCustomElement(ele); }}}}else {
		        // Parse custom tags (more on custom tags later)delegate.parseCustomElement(root); }}Copy the code

In this article, we will only look at parseDefaultElement. This method is the own tag of Spring IOC. I will make a separate article about the custom tag:

The code block13
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		// Parse the import tag
		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);
		}
		// How to parse beans?
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recursedoRegisterBeanDefinitions(ele); }}Copy the code

In fact, you can see that in spring XML, only import, alias, bean, beans belong to IOC itself, and the rest belong to custom tags. Now let’s look at parsing the import label importBeanDefinitionResource method:

The code block14
// Parse the import tag
protected void importBeanDefinitionResource(Element ele) {
		// Get the location property value and check if it is empty (new version property name is changed to resource)
		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		if(! StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele);
			return;
		}
		// If the location has something like ("${user.dir}"), you need to parse this first
		location = getEnvironment().resolveRequiredPlaceholders(location);
		
		Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

		// Determine whether location is a relative path or an absolute path
		boolean absoluteLocation = false;
		try {
			// The logic here is to see if the location starts with classpath*: or classpath: or if the location is a URL or if you are an absolute or relative path based on the scheme of the URI
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		} catch (URISyntaxException ex) {
			
		}

		// If it is an absolute path, load the parse Resource directly with loadBeanDefinitions in xmlBeanDefinitionReader
		// If it is not an absolute path, calculate the relative path, and then load the parse Resource with loadBeanDefinitions in xmlBeanDefinitionReader
		if (absoluteLocation) {
			try {
				// Note that getReaderContext().getreader () returns the xmlBeanDefinitionReader object
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]"); }}catch (BeanDefinitionStoreException ex) {
				getReaderContext().error(
						"Failed to import bean definitions from URL location [" + location + "]", ele, ex); }}else {
			// Resolve Resource relative to path
			try {
				int importCount;
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				if (relativeResource.exists()) {
				        // Use the loadBeanDefinitions method above to determine the relative path of the Resource
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				else {
				        // If relative path parsing fails, try URL parsing
					String baseLocation = getReaderContext().getResource().getURL().toString();
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]"); }}catch (IOException ex) {
				getReaderContext().error("Failed to resolve current resource location", ele, ex);
			} catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, ex); }}// After parsing, the listener is activated, but in this version, nothing is done, because the final method is an empty implementation, so it does not matter here
		Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}
Copy the code

So this seems like a very long method, and it’s a little bit complicated, but basically you take the location attribute of the import tag and you check it, and then you check whether location is a relative path or an absolute path, and then you parse it differently depending on the path, But ultimately you’re going to have to use loadBeanDefinitions(Resource Resource), The check for import loop references in the loadBeanDefinitions(EncodedResource EncodedResource) method that is then called in this method refers to this one, as mentioned in block 5.

Parsing the import tag, let’s look at the code for parsing the alias tag:

The code block15
        protected void processAliasRegistration(Element ele) {
		// Get the alias and name attributes in the alias tag
		String name = ele.getAttribute(NAME_ATTRIBUTE);
		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
		// Validates the name attribute. If it is empty, an exception is thrown in the error method
		boolean valid = true;
		if(! StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele);
			valid = false;
		}
		// Validates the alias attribute. If it is empty, an exception is thrown in the error method
		if(! StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele);
			valid = false;
		}
		// If valid, register the alias
		if (valid) {
			try {
			        // The specific alias parsing logic will be covered in a later article
				getReaderContext().getRegistry().registerAlias(name, alias);
			} catch (Exception ex) {
				getReaderContext().error("Failed to register alias '" + alias +
						"' for bean with name '" + name + "'", ele, ex); } getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); }}Copy the code

As for beans tag is to use the aforementioned doRegisterBeanDefinitions method, namely code block 11, actually is the recursive analysis. The most important processBeanDefinition method, which parses a bean label into a concrete BeanDefinition object, is shown below

The code block16
        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
                // Parsing the bean label is also the most important logic
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if(bdHolder ! =null) {
			// 1. Parse the custom label under the default label
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 2. Register beanDefiniton 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(newBeanComponentDefinition(bdHolder)); }}Copy the code

There we see the first line by first assigned to parse the bean label method, this also is the most important, it is a direct call his class parseBeanDefinitionElement (Element ele, BeanDefinition containingBean). The second argument is passed null, so let’s look at this method:

The code block17
        // Start parsing the 
      
        tag
      
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
		// 1. Get the bean id and name attributes
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

                // Put name into the collection, which will later be registered as an alias
		List<String> aliases = new ArrayList<String> ();if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		// 2. If there is no id attribute, the first of the name attributes is used as the bean's unique identifier name in the container
		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"); }}// 3. Check whether the bean name is unique
		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}

		// This is the beanDefinition
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if(beanDefinition ! =null) {
			// If beanName is still empty or a string is empty, beanName is generated as a rule (not important)
			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;
	}
Copy the code

The above code blocks are divided into four parts, the first three parts are very simple, let’s take a look at the fourth part of the parseBeanDefinitionElement (ele, beanName containingBean) this overloads, note at this point the third parameter is still passed is null:

The code block18
        // containingBean is null
	public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {
		this.parseState.push(new BeanEntry(beanName));
		// 1. Get the full path of the bean class name
		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}

		try {
			// get the id of the parent bean
			String parent = null;
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			// 3. So far, all paths of parent and class are found
			// bd has only class and parentName and parentName
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			// 4. Set attributes in the bd tag
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

			parseMetaElements(ele, bd);
			// 5. Lookup -method and replace-method,
			// lookup-method = @lookup; // lookup-method = @lookup
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

			Handle constructor-arg, property, qualifier tags
			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;
	}
Copy the code

Now that there are methods to parse each tag, let’s take a look at the method finally called in the third block above:

The code block19
        public static AbstractBeanDefinition createBeanDefinition(
			String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {

		GenericBeanDefinition bd = new GenericBeanDefinition();
		// The parent beanDefnition name
		bd.setParentName(parentName);
		if(className ! =null) {
			if(classLoader ! =null) {
				bd.setBeanClass(ClassUtils.forName(className, classLoader));
			}
			else{ bd.setBeanClassName(className); }}return bd;
	}
Copy the code

As you can see, our bean tag is ultimately resolved to an instance of GenericBeanDefinition, and almost all of its member properties come from AbstractBeanDefinition, We already mentioned these properties at the beginning of this article, so you can check them out if you don’t understand. Then look at the code in block 18, fourth:

The code block20
        public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
			BeanDefinition containingBean, AbstractBeanDefinition bd) {

		/ / the scope attribute
		if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
			this.readerContext.warning("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) {
			bd.setScope(containingBean.getScope());
		}
		
		/ / the abstract properties
		if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
			bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
		}
		
		/ / lazy - init properties
		String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
		if (DEFAULT_VALUE.equals(lazyInit)) {
			lazyInit = this.defaults.getLazyInit();
		}
		bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
		
		String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
		bd.setAutowireMode(getAutowireMode(autowire));
		
		// This property is obsolete from 3.0, and can be injected with the constructor
		String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
		bd.setDependencyCheck(getDependencyCheck(dependencyCheck));

		// The tokely-on attribute corresponds to the current annotation DependOn.
		if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
			String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
			bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
		}

		// Autowire-candidate property, which indicates whether this bean can be used as an object to inject into other beans
		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));
		}

		// paimary, referring to the preference of the bean to be used by autowiring
		if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
			bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
		}

		/ / init - method attribute
		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); }}/ / destory properties
		Init method and deStory are the same as post Construct and pre deStory annotations
		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); }}/ / factory - method attribute
		if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
			bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
		}
		/ / factory - bean properties
		if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
			bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
		}

		return bd;
	}
Copy the code

The code for the methods 5 and 6 in block 18 is very simple and will not be posted to keep the article short. In using delegation parseBeanDefinitionElement after parsing XML attributes in the already have been read to beanDefinition object above. Next up is decorating custom properties and custom tags, and I’ll talk about that in custom tags. After decorating the custom, you register the BeanDefinition object, and the PARSING of the XML is complete.

ClassPathApplicationContext and BeanDefinitionReader

I don’t know if you remember the code at the beginning of the last post:

public static void main(String[] args) {
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("classpath*:application-context.xml");
        ATest aTest = applicationContext.getBean(ATest.class);
        aTest.doSomeThing();
}
Copy the code

This code ClassPathApplicationContext will our parses XML loaded into beanDefinition objects, and transformed into the final bean, Now that we know how XmlBeanDefinitionReader parses XML into beanDefinition objects, where does this parsing take place while this code is running? We take a look at the structure of the ClassPathApplicationContext method:

The code block21
        public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		// Call the method below
		this(new String[] {configLocation}, true.null);
	}
	
	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		// Parse the configuration with [classpath*:application-context.xml]
		setConfigLocations(configLocations);
		if (refresh) {
		        // Call the refresh method belowrefresh(); }}Copy the code
Note: I'm only showing the first few lines of Refresh for now, because the rest is not relevant to what we're talking about so farCopy the code
The code block22
        @Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 1. Preparation before loading
			prepareRefresh();
			// 2. Get a new beanFactory instance
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 3. Initialize beanFactory and set various Settings for it
			prepareBeanFactory(beanFactory);
Copy the code

Put out only the beginning of that a few lines of code, because we now say, also with the second sentence, we direct the obtainFreshBeanFactory of 2, it is also in AbstractApplicationContext,

The code block23
        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
                / / the key
		refreshBeanFactory();
		
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ":" + beanFactory);
		}
		return beanFactory;
	}
Copy the code

The focus is on the first line of this method, this method is also AbstractApplicationContext, but it is an abstract method, the real implementation is in AbstractRefreshableApplicationContext, Let’s look directly at the refreshBeanFactory method here:

The code block24
        @Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			// Load the BeanDefinition
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory; }}catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code

Only a block of code I gave above notes, that is also an abstract method, the real implementation is in subclasses AbstractXmlApplicationContext, we to AbstractXmlApplicationContext:

The code block25
        @Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		XmlBeanDefinitionReader (XmlBeanDefinitionReader
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		
		/ / 2. It is in AbstractXmlApplicationContext, its parent class inherited DefaultResourceLoader already, also
		// Implements ResourcePatternResolver
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	
		// 3. Set the validation model
		initBeanDefinitionReader(beanDefinitionReader);
		// 4. Load Resource
		loadBeanDefinitions(beanDefinitionReader);
	}
Copy the code

Here we can see directly, use XmlBeanDefinitionReader ClassPathXmlApplicationContext is to load the parsing of the Resource, let’s take a look at the above code block 3 in the code

The code block26
        protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
		reader.setValidating(this.validating);
	}
Copy the code

This calls the internal method of XmlBeanDefinitionReader. Note that the this. valtionReader argument contains the value true.

The code block27
        public void setValidating(boolean validating) {
		this.validationMode = (validating ? VALIDATION_AUTO : VALIDATION_NONE);
		this.namespaceAware = ! validating; }Copy the code

This is where the validationMode value, which appears in blocks 7 and 8, is assigned to the application to find out what parsing method to use. Finally, let’s look at the code at 4 in block 25

The code block28
        protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if(configResources ! =null) {
			reader.loadBeanDefinitions(configResources);
		}
		// Actually go here, because configResources above is null
		String[] configLocations = getConfigLocations();
		if(configLocations ! =null) { reader.loadBeanDefinitions(configLocations); }}Copy the code

This method calls the XmlBeanDefinitionReader method we talked about earlier. Now, we’ve done our best to define how XML parses to BeanDefinition. Let’s summarize what we’ve talked about so far in this graph:

We can see that this is, in fact, the bottom part, and in the next part we’ll talk about how to go from BeanDefinition to Bean instance.