[TOC]


This note mainly records the following contents:

Using the ClassPathXmlApplicationContext, through in the XML to register a bean, tracking code, to understand it from a configuration file < bean > tag, Detailed procedure for loading beanDefinitionMap into the BeanFactory registry.

The code shown has extracted some core methods, removed some default Settings and log output, and also removed most error exceptions. If you want to see the detailed code, annotations and demo, you can download the notes I uploaded at 📒

Code cloud Gitee address

Making the address

By reading the source code process, understand the designer’s design ideas and learning from it, have a basic understanding of Spring.


infrastructure

We’ll start by showing you how to register and use beans in code:

config.xml

<?xml version="1.0" encoding="UTF-8"? >
<beans>
	<bean id="book" class="domain.SimpleBook"/>
</beans>
Copy the code

Define a simple class:

SimpleBook.java

public class SimpleBook {

	private int id;

	private String name = "Default Name";

}
Copy the code

Using the ClassPathXmlApplicationContext bean was obtained from the XML configuration file:

public static void main(String[] args) {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
	SimpleBook book = context.getBean(SimpleBook.class);
	System.out.println(book.getName());
}
Copy the code

When the code runs properly, the console prints:

Default Name
Copy the code

Normally, we need to instantiate an object by new initialization, memory allocation, etc. But with the Spring container, we can turn SimpleBook over to Spring to manage without having to do new SimpleBook in our code. Object instances ~ can be easily obtained by automatic injection (such as the @Autowire annotation) or, as in the example, by obtaining context objects and then using the getBean() method.


ClassPathXmlApplicationContext

ClassPathXmlApplicationContext inheritance system structure diagram:

The Diagrams are displayed through the Diagrams function of the IDEA editor. Right-click the current class, you can see the inheritance system, which classes are inherited and which interfaces are referenced, which is convenient for us to understand ~

ClassPathXmlApplicationContext inherited from AbstractApplicationContext, And AbstractRefreshableApplicationContext is AbstractApplicationContext abstract subclass, the use of the class is DefaultListableBeanFactory registered factories, This registered factory is also very important and will be described later.

In a nutshell,DefaultListableBeanFactory 是 SpringRegistration and LoadingbeanBy default, it will be registeredbeanIn thebeanDefinitionMapforkey-valueForm storage.

As you can see in the upper right corner of the image, ResourceLoader is its top-level interface, indicating that this class implements resource loading.

Constructor code:

public ClassPathXmlApplicationContext(
		String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
		throws BeansException {
	super(parent);
	// Comment 1.1 Getting resource files
	setConfigLocations(configLocations);
	if(refresh) { refresh(); }}Copy the code

The constructor

As you can see from this line, the subclass constructor calls the parent class constructor:

super(parent)

Has been tracking code, found starting from the subclass, along the parent class has been called up, until the AbstractApplicationContext:

public AbstractApplicationContext(a) {
	this.resourcePatternResolver = getResourcePatternResolver();
}

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
	this(a); setParent(parent); }Copy the code
protected ResourcePatternResolver getResourcePatternResolver(a) {
	return new PathMatchingResourcePatternResolver(this);
}
Copy the code

Initialization functions are used to set the resource matching handler. The ResourcePatternResolver interface defines a strategy for parsing location patterns (for example, Ant style path patterns) into resource objects. Specific implementation class is PathMatchingResourcePatternResolver (path matching parser resource model, we used to parse the incoming path config. The XML)


Set the configuration file path

org.springframework.context.support.AbstractRefreshableConfigApplicationContext

public void setConfigLocations(@Nullable String... locations) {
	if(locations ! =null) {
		Assert.noNullElements(locations, "Config locations must not be null");
		1.2 Put the configuration resource path into the configLocations array
		this.configLocations = new String[locations.length];
		for (int i = 0; i < locations.length; i++) {
			this.configLocations[i] = resolvePath(locations[i]).trim(); }}else {
		this.configLocations = null; }}Copy the code

A resolvePath resolves a given path and replaces placeholders with placeholder placeholders

For example, new ClassPathXmlApplicationContext (” classpath: config. XML “); , you need to resolve the classpath to the correct path.

protected String resolvePath(String path) {
	return getEnvironment().resolveRequiredPlaceholders(path);
}
Copy the code

We have different operating environments, such as dev, test or Prod, and the loaded configuration files and properties should be different. In this case, we need to use Environment to distinguish between them.

The Spring environment and properties are made up of four parts:

  • Environment: Environment, byProfile å’Œ PropertyResolverCombination.
  • Profile: configuration file, which can be interpreted as the attributes and values of multiple configuration groups in a containerbean, only activeprofile, its corresponding group attributes andbeanBefore it gets loaded
  • PropertySource: Attribute source, usedCopyOnWriteArrayListArray for property pairskey-valuestored
  • PropertyResolver: property resolver, which is used to parse properties

Environment

First look at StandardServletEnvironment inheritance system:

As you can see, the top level interface is the PropertyResolver, which is used to parse properties, and ultimately parse the calling method

PropertyPlaceholderHelper.replacePlaceholders

public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
	Assert.notNull(value, "'value' must not be null");
    // Replace all placeholders in the format {@code ${name}} with the returned value
	return parseStringValue(value, placeholderResolver, null);
}
Copy the code

Profile

Using this attribute, you can deploy two sets of configurations in the configuration file at the same time, which are applicable to the production environment and the development environment. In this way, you can easily switch the development and deployment environment, and often change different databases or configuration files.

Demo :(referenced in article 4 of resources)

<! -- Test environment configuration file -->
<beans profile="test">
    <context:property-placeholder location="classpath:test/*.properties, classpath:common/*.properties" />
</beans>

<! -- Production environment configuration file -->
<beans profile="production">
    <context:property-placeholder location="classpath:production/*.properties, classpath:common/*.properties" />
</beans>

<! -- Development environment configuration file -->
<beans profile="development">
    <context:property-placeholder location="classpath:dev/*.properties, classpath:common/*.properties" />
</beans>
Copy the code

There are two ways to set which configuration to use:

â‘  Set the parameters in web. XML

<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>test</param-value>
</context-param>
Copy the code

â‘¡ Set when the code starts

context.getEnvironment().setActiveProfiles("test");
Copy the code

Property

Property official comment description:

/**
 * A description of a JavaBeans Property that allows us to avoid a dependency on
 * {@code java.beans.PropertyDescriptor}. The {@code java.beans} package
 * is not available in a number of environments (e.g. Android, Java ME), so this is
 * desirable for portability of Spring's core conversion facility.
 *
 **/It allows us to avoid using {@codeDependency on java.bean.propertyDescriptor}. Because {@codeJava. bean}packageIt is not available in many environments (Android, Java ME, for example), so this is ideal for the portability of Spring's core transformation tools.Copy the code

You can find it in abstractenvironment. Java. When setting env, you create a MutablePropertySources object that holds properties:

private final MutablePropertySources propertySources = new MutablePropertySources()

private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);
            
public AbstractEnvironment(a) {
	customizePropertySources(this.propertySources);
}
Copy the code

PropertySource interface

The inheritance system is shown as follows:

From the PropertySource inheritance, the customizePropertySources method is called all the way up from the subclass:

AbstractEnvironment -> StandardServletEnvironment -> StandardEnvironment

Finally, the properties are stored in StandardEnvironment using the CopyOnWriteArrayList array

protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
	propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
Copy the code

For example, propertySourceList will store the system’s parameters:

These parameters can then be retrieved in the context of the launched application

((MutablePropertySources)((StandardEnvironment)context.environment).propertySources).propertySourceList
Copy the code

summary

Just a series of prelude work, just to identify the path resources and load system parameters

  • Setup constructor
  • Identify path variables
  • Setting environment ParametersMain is:EnvironmentSystem, and inpropertySourcesThe runtime parameters are saved in

Bean parsing and registration

There is an important method for parsing and registering Spring beans, refresh()

AbstractApplicationContext.refresh()

public void refresh(a) throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();
		// Tell the subclass to refresh the internal bean factory.
		// Class registration to bean Factory is also in this step
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);
		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);
			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);
			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);
			// Initialize message source for this context.
			initMessageSource();
			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();
			// Initialize other special beans in specific context subclasses.
			onRefresh();
			// Check for listener beans and register them.
			registerListeners();
			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);
			// Last step: publish corresponding event.
			finishRefresh();
		}
		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}
			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();
			// Reset 'active' flag.
			cancelRefresh(ex);
			// Propagate exception to caller.
			throw ex;
		}
		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...resetCommonCaches(); }}}Copy the code

This method is tracked and analyzed below.


The prepareRefresh is ready for an update

This method prepares this context for refreshing, setting its start date and active flag, and performing initialization of any property sources.

protected void prepareRefresh(a) {
	// Switch to active.
	// Initialize any placeholder property sources in the context.
	initPropertySources();
	// Validate that all properties are marked as required are resolvable:
	// see ConfigurablePropertyResolver#setRequiredProperties
	getEnvironment().validateRequiredProperties();
	// Allow for the collection of early ApplicationEvents,
	// to be published once the multicaster is available...
	this.earlyApplicationEvents = new LinkedHashSet<>();
}
Copy the code

The specific verification method

org.springframework.core.env.AbstractPropertyResolver#validateRequiredProperties

public void validateRequiredProperties(a) {
	MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
	for (String key : this.requiredProperties) {
		if (this.getProperty(key) == null) { ex.addMissingRequiredProperty(key); }}if(! ex.getMissingRequiredProperties().isEmpty()) {throwex; }}Copy the code

As you can see, the validation logic iterates over requiredProperties, which is a Set of characters that is null by default, indicating that no elements need to be validated. If there is a value in the list and the corresponding environment variable is null based on the key, an exception will be thrown, causing Spring container initialization to fail.


Customize environment variable verification

Now that we have a requireProperties list, we can add the required environment variables to it:

  • Create a class that inherits fromAnnotationConfigServletWebServerApplicationContext, overloadinginitPropertySources
  • When the application starts, set your new class as the application context (application.setApplicationContextClass(CustomContext.class);)

For example :(cited in article 5 of resources)

public class CustomApplicationContext extends AnnotationConfigServletWebServerApplicationContext {
    @Override
    protected void initPropertySources(a) {
        super.initPropertySources();
        // use "MYSQL_HOST" as the environment variable that must be verified at startup
        getEnvironment().setRequiredProperties("MYSQL_HOST"); }}public static void main(String[] args) {
    SpringApplication springApplication = new SpringApplication(CustomizepropertyverifyApplication.class);
    springApplication.setApplicationContextClass(CustomApplicationContext.class);
    springApplication.run(args);
}
Copy the code

By adding custom validation values, you can pre-validate Spring applications when they start


To obtainbeanThe container

In this line of code ConfigurableListableBeanFactory the beanFactory = obtainFreshBeanFactory ();

Specifically called is:

org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

protected final void refreshBeanFactory(a) throws BeansException {
	// On update, if it already exists, the previous bean is cleaned up and the old bean container is closed
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		customizeBeanFactory(beanFactory);
		// Comment 1.3 Start loading (bean registration)
		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

This entry method is important, creating a new bean container and resolving beans, and registering the beans in the container.


BeanFactory inheritance

In this example, and in most cases, use the bean container are DefaultListableBeanFactory, since the introduction of its inheritance system:

As you can see, the inheritance system is very large, inheriting multiple registries and implementing multiple interfaces. Singleton registries and Alias registries are commonly used. These two concepts are also very large. We’ll talk about it later when we have a chance.


BanFactory custom

Here’s how: With this method, you can customize the factory and let subclasses configure it freely:

org.springframework.context.support.AbstractRefreshableApplicationContext#customizeBeanFactory

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
	if (this.allowBeanDefinitionOverriding ! =null) {
		// The default is false and overwriting is not allowed
		beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.allowCircularReferences ! =null) {
		// The default is false and circular references are not allowed
		beanFactory.setAllowCircularReferences(this.allowCircularReferences); }}Copy the code

Bean loading and parsing

The core method is this:

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory. support.DefaultListableBeanFactory)

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	// Create a new XmlBeanDefinitionReader for the given BeanFactory
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	// Configure the bean definition reader with this context's
	// resource loading environment.
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	// Allow a subclass to provide custom initialization of the reader,
	// Then proceed with actually loading the bean definitions.
	initBeanDefinitionReader(beanDefinitionReader);
	loadBeanDefinitions(beanDefinitionReader);
}
Copy the code

In parsing XML, two inheritance systems are used: EntityResolver and BeanDefinitionReader


EntityResolver

Interface is the full path: org. XML.. Sax EntityResolver, specific analytical method is:

org.springframework.beans.factory.xml.ResourceEntityResolver#resolveEntity

This method is used to parse schemas and DTDS, but parsing XML is not what I want to know, so I’ll skip ~ for now


BeanDefinitionReader

The top-level interface is BeanDefinitionReader, a bean-definition reader for XML Bean definitions. Delegate the XML document that is actually read to the implementation.

The purpose of these two classes is to turn XML into an input stream, and you can follow them further


Loading configuration files

Entry method :(because there are multiple methods with the same name, so when copying the path, the type of the parameter is also copied)

org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)

The core method is these two lines

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    // Get resource file (resource loader identifies resource file from path)
    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location)
    // Comment 1.6 Loads beans from resource files
    intcount = loadBeanDefinitions(resources); ...}Copy the code

Once you get the resource file, you parse the resource file (the config.xml that was passed in the first place) and convert it to Document

Tracing the code, you can see that the parsed Resource file is wrapped from Resource as EncodeResouce, adding character encoding (null by default) to the input stream, embodiments the design-decorator pattern

The core method is the following two lines:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.En codedResource)

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	// Comment 1.7 Get the input stream from the resource file
	InputStream inputStream = encodedResource.getResource().getInputStream();
	InputSource inputSource = new InputSource(inputStream);
	return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
Copy the code

Bean parsing

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
	throws BeanDefinitionStoreException {
	// comment 1.8 Parse resource files into documents
	Document doc = doLoadDocument(inputSource, resource);
	// Note 1.10 Parses elements from doc and resources to register with bean Factory
	int count = registerBeanDefinitions(doc, resource);
	if (logger.isDebugEnabled()) {
		logger.debug("Loaded " + count + " bean definitions from " + resource);
	}
	return count;
}
Copy the code

In the doLoadDocument() method, the resource file is parsed into a Docuemnt document

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	/ / use BeanDefinitionDocumentReader DefaultBeanDefinitionDocumentReader instantiated
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	// Record the number of beanDefinitions loaded before statistics
	int countBefore = getRegistry().getBeanDefinitionCount();
	/ / load and registered bean, is a registered factories DefaultListableBeanFactory here
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	// Record the number of beanDefinitions loaded (new value - old value)
	return getRegistry().getBeanDefinitionCount() - countBefore;
}
Copy the code

How to convert document and documentReader to document initialization is not explained here

Want to say is bean container DefaultListableBeanFactory parse the document below

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

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);
			// We cannot use Profiles.of(...) since profile expressions are not supported
			// in XML config. See SPR-12458 for details.
			if(! getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isDebugEnabled()) {
					logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return; }}}PreProcess and postProcess are empty methods. These methods are left to subclasses to override and represent the design pattern-template method
	preProcessXml(root);
	// Annotate the 1.11 core method to parse the doc element
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);
	this.delegate = parent;
}
Copy the code

Can be seen from the above, before parsing, if the namespace is http://www.springframework.org/schema/beans at the beginning, and will check the profile properties

Once the validation passes, the doc element is formally parsed

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	if (delegate.isDefaultNamespace(root)) {
		// Comment 1.12 Traversing the node list in doc
		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)) {
					// Note 1.13 bean registry that recognizes default tags
					// Depending on the element name, call a different loading method to register the bean
					parseDefaultElement(ele, delegate);
				}
				else{ delegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}Copy the code

In this step, the attributes we configured in the XML can be mapped to the Document object for later use in the process


Default tag parsing

I won’t go into detail in this section, but I’ll cover it in a later article, so let’s take a quick look at how the default tag is resolved in code

  • IMPORT: IMPORT labels
  • ALIAS: indicates an ALIAS label
  • BEAN:beanThe label
  • NESTED_BEANS:beansTags (nestedbeans)

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recursedoRegisterBeanDefinitions(ele); }}Copy the code

Let’s see how to parse bean tags


Bean label parsing

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// Comment 1.15 Parsing elements of bean names
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if(bdHolder ! =null) {
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance. (Note 1.16 Register the last decorated 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

Here’s what the key methods do


Get id and name

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
	// Get the ID attribute
	String id = ele.getAttribute(ID_ATTRIBUTE);
	// Get the NAME attribute
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	List<String> aliases = new ArrayList<>();
	if (StringUtils.hasLength(nameAttr)) {
		// Name according to,; segmentation
		String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		aliases.addAll(Arrays.asList(nameArr));
	}
	String beanName = id;
	if(! StringUtils.hasText(beanName) && ! aliases.isEmpty()) {// If id is not specified, the first value of name is used as id
		beanName = aliases.remove(0);
	}
	/ / null by default
	if (containingBean == null) {
		// Check if the name is unique, and throw an error if the ID is duplicated
		// Internal usedNames is a HashSet that will store loaded names and aliases
		checkNameUniqueness(beanName, aliases, ele);
	}
	AbstractBeanDefinition {GenericBeanDefinition} // Put a public property in AbstractBeanDefinition
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if(beanDefinition ! =null) {
		if(! StringUtils.hasText(beanName)) {if(containingBean ! =null) {
				// If both id and name are empty, spring will generate a default name for it
				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;
}
Copy the code

The process of getting the ID and name attributes is clear step by step as you follow the code comments

The main workflow of this method is as follows:

  • Extract elements fromid nameattribute
  • All other attributes are further parsed and unified encapsulated toGenericBeanDefinitionType
  • detectedbeanIs not specifiedbeanNameUse default rule generationbeanName
  • Encapsulate the obtained information toBeanDefinitionHolderIn the instance of the

Parsing of other attributes in the tag

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)

public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    parseMetaElements(ele, bd);
    parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
    parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    parseConstructorArgElements(ele, bd);
    parsePropertyElements(ele, bd);
    parseQualifierElements(ele, bd);
    bd.setResource(this.readerContext.getResource());
    bd.setSource(extractSource(ele));
    return bd;
}
Copy the code

Initialize BeanDefiniton in this method :(the implementation is a subclass of GenericBeanDefinition.)

BeanDefinitionReaderUtils.createBeanDefinition(parentName, className, this.readerContext.getBeanClassLoader())

public static AbstractBeanDefinition createBeanDefinition( @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
	GenericBeanDefinition bd = new GenericBeanDefinition();
	bd.setParentName(parentName);
	if(className ! =null) {
		if(classLoader ! =null) {
			bd.setBeanClass(ClassUtils.forName(className, classLoader));
		}
		else{ bd.setBeanClassName(className); }}return bd;
}
Copy the code

The following is to parse the content of other tags, and then will patch the hole ~


BeanDefinition inheritance system

As can be seen from the figure, BeanDefinition is an interface, GenericBeanDefinition, RootBeanDefinition, ChildBeanDefinition, All three of these inherit AbstractBeanDefinition.

Where BeanDefinition is the internal representation of the configuration file

element tag in the container.

The

element tag has class, scope, and lazy-init configuration attributes, while the BeanDefinition provides the corresponding beanClass, scope, and lazyInit attributes, which correspond to each other.


BeanDefinitionHolder modification

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#decorateBeanDefinitionIfRequired(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinitionHolder, org.springframework.beans.factory.config.BeanDefinition)

public BeanDefinitionHolder decorateBeanDefinitionIfRequired( Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {
	// The third argument in the method is the parent bean
	// When analyzing a nested configuration, we need to pass it in order to use the scope attribute of the parent class, in case the child class does not have scope, we can use the scope attribute of the parent class
	BeanDefinitionHolder finalDefinition = definitionHolder;

	// Decorate based on custom attributes first.
	NamedNodeMap attributes = ele.getAttributes();
	// Iterate over all attributes to modify attributes
	for (int i = 0; i < attributes.getLength(); i++) {
		Node node = attributes.item(i);
		finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
	}

	// Decorate based on custom nested elements.
	NodeList children = ele.getChildNodes();
	// Iterate through all the child nodes, modifying the child elements
	for (int i = 0; i < children.getLength(); i++) {
		Node node = children.item(i);
		if(node.getNodeType() == Node.ELEMENT_NODE) { finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); }}return finalDefinition;
}
Copy the code

After the previous general attribute parsing, this step is mainly used to complete the parsing of the custom tag element, here continues to leave a hole ~


Bean registered

Through the painstaking parsing operations in the above columns, we have finally arrived at a way to register bean information

org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition

public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {
		// Register bean definition under primary name.
		/ / comment 1.17 in DefaultListableBeanFactory beanDefinitionMap add bean definition
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if(aliases ! =null) {
			for(String alias : aliases) { registry.registerAlias(beanName, alias); }}}Copy the code

Also said above, here is the use of the bean container is DefaultListableBeanFactory, registration method key operation when the following two lines of code:

org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
	this.beanDefinitionMap.put(beanName, beanDefinition);
	this.beanDefinitionNames.add(beanName);
}
Copy the code

At this point, I’m going tobeanPut information intobeanDefinitionMap, the operation of class registration is completed

In order to describe the integrity of the code logic, the following methods are briefly introduced.


prepareBeanFactory

Prepare class loader environment, to the front to get to the beanFactory (ConfigurationListableBeanFactory) related Settings, including this, post – processors, etc


postProcessBeanFactory

When all bean definitions are loaded, but the bean has not been instantiated, its internal bean container is modified after the standard initialization of the application context.

This allows you to register special BeanPostProcessors, etc., in a particular ApplicationContext implementation.

This is also an empty method, to be implemented by subclasses


invokeBeanFactoryPostProcessors

Instantiate and call all registered BeanFactoryPostProcessorBean, these are the post-processor, processing type is the BeanFactory, the Spring container allows before instantiation bean, bean information and modify its properties.

This gives the user one last chance to modify the bean information before instantiation.

Also, execution can be ordered by Order value, depending on whether these processors implement PriorityOrdered, Order interfaces.


registerBeanPostProcessors

Instantiate and register all post-handlers. Unlike the above method, this method handles beans of type and, like the above method, also has the concept of priority


initMessageSource

Initialize the message source for this context


initApplicationEventMulticaster

Initializes the event multicast program for this context


onRefresh

Template methods that can be overridden to add context-specific refresh work.

Call the initialization of a particular bean before instantiating the singleton. (fog, don’t know what special bean, leave a pit =-=)

This implementation is null.


registerListeners

Check the listener beans and register them

The EventListener type is java.util.eventlistener


finishBeanFactoryInitialization

Complete the initialization of the bean container, instantiating all remaining (non-lazy-initialized) singletons


finishRefresh

Finally, publish the corresponding event

The event type is java.util.eventobject


resetCommonCaches

The last step in real registration is to clear the cache

Reset the public introspection cache in the Spring core, because we may never need the singleton bean metadata again


conclusion

The notes in this chapter simply document how a bean is loaded from XML into the registry of the bean container, goes through many lines of code, and finally figures out the call link.

Here’s a summary of the core loadBeanDefinitions(beanFactory) workflow:

â‘  Read the configuration file

  • Encapsulating resource files: Obtain the path file and encapsulate it asEncodeResource
  • Get input streamFrom:ResourceTo obtain the correspondingInputStreamAnd constructInputSource
  • Passing parametersBy constructionInputSourceInstance andResourceInstance, passed todoLoadBeanDefinitionsmethods

(2) load the beans

  • Access toXMLValidation mode for resource files
  • loadingXMLThe resource file is parsed into the correspondingDocumentThe document: There are many in itNodeNode information, which holds the configuration information we wrote
  • According to theDocumentA fileBeanInformation parsing

â‘¢ Bean label parsing and registration

  • entrustBeanDefinitionDelegateOf the classparseBeanDefinitionElementmethods: Parses the element and returnsBeanDefinitionHolderAn instance of theclass,name,id,aliasSuch attributes)
  • Parsing labels: Determine the label type and determine whether the default or custom labels are parsed
  • rightbdHodlerTo register: After parsing, registerbeanInformation, registration operations delegated toBeanDefinitionReaderUtils çš„ registerBeanDefinitionmethods
  • Send response event: Notifies associated listeners, notifiesbeanThe container has been loaded

See you in the next note


Record on pit

Javadoc generation failed. Generated Javadoc options file (useful for troubleshooting)

The gradle file contains the following configuration: gradle: gradle: gradle: gradle: gradle: gradle: gradle: gradle

tasks.withType(Javadoc) {
    options.addStringOption('Xdoclint:none'.'-quiet')
    options.addStringOption('encoding'.'UTF-8')}Copy the code

The resources

1, spring – analysis/note/spring. The md

2. Spring Framework 5.0.0m3

3. Deep analysis of Spring source code/Hao Jia. — Beijing: Posts and Telecommunications Press

Profile configuration with Spring3.1 enables different environments to load different profiles

5, one of the extension practices of spring4.1.8: custom environment variable validation

Portal:

  • Spring source learning – environment preparation

  • (1) The infrastructure of the container

  • Spring source code learning (2) default tag parsing

  • Spring source code learning (3) custom tags

  • Spring source code learning (four) bean loading

  • Spring source code learning (5) loop dependency

  • Spring source code learning (six) extension function part 1

  • Spring source code learning (seven) extension features part 2

  • Spring source learning (eight) AOP use and implementation principle

  • Spring source code learning (9) Transaction Transaction

  • (10) Spring MVC