Introduction: Spring source code inheritance, nesting level is very much, it is very easy to read dizzy, partners in the article must follow the ideas of the article to their own source point, take a look, and see several times. It becomes clearer and clearer. Let’s get started

1. The core classes of Spring

1.1. The BeanFactory

In Spring’s IOC container factory, BeanFactory is the top-level interface class that defines the basic IOC container specification. BeanFactory has three subclasses: ListableBeanFactory (list), HierarchialBeanFactory (hierarchical), AutowireCapableBeanFactory (can be automatically injected), but can be found from the figure, Their final implementation class is DefaultListableBeanFactory, he achieved all interfaces, why to define the multi-layered interface? Looking at the source code and description, it can be found that each interface has its own application occasions, which is mainly to distinguish between Spring’s internal restrictions on object data access in the process of object transfer and transformation during operation. For example, the ListableBeanFactory interface indicates that these beans are listable; HierarchialBeanFactory means that these beans are hierarchically related, meaning that each bean may have a parent; AutowireCapableBeanFactory define the bean’s automatic assembly rules. Together, these four interfaces define the collection of beans, the relationships between beans, and the behavior of beans.

Public interface BeanFactory {/** * Escape the factoryBean definition, because if you retrieve a FactoryBean by its name, the object you get is a factory-generated object, * If you need to retrieve the project itself, Need to escape */ String FACTORY_BEAN_PREFIX ="&"; Object getBean(String name) throws BeansException; /* getBean(String name) throws BeansException; /** * Get the bean instance according to the bean name and class type, add type safety verification mechanism * If need to get the project itself, */ <T> T getBean(String name, Class<T> requiredType) throws BeansException; /** * getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; */ Boolean containsBean(String name); */ Boolean containsBean(String name); / * * *, according to the names of the bean bean instance, and to determine whether the bean singleton pattern * / Boolean isSingleton (String name) throws NoSuchBeanDefinitionException; / * * *, according to the names of the bean bean instance, and decide if the bean is archetypal pattern * / Boolean isPrototype (String name) throws NoSuchBeanDefinitionException; Boolean isTypeMatch(String name, Class<? >typeToMatch) throws NoSuchBeanDefinitionException; /** * the class type of the bean instance */ class <? > getType(String name) throws NoSuchBeanDefinitionException; */ String[] getAliases(String name); /** * getAliases(String name); }Copy the code

The basic behavior of the IOC container is defined in the BeanFactory, regardless of how the bean is defined and loaded. Just as we only care about the product objects we get in the factory, this interface doesn’t care about how the factory makes the product.

To understand how the factory generates objects, we need to look at the implementation of the IOC container. Spring provides many container implementations. Such as XmlBeanFactory, ClasspathXmlApplicationContext etc, including XmlBeanFactory is aimed at the most basic implementation of the IOC container, The IOC container can read the BeanDefinition defined by the XML file. If XmlBeanFactory is the best in the container, the ApplicationContext should be the best in the container.

ApplicationContext is an advanced IOC container provided by Spring. In addition to providing the basic functions of an IOC container, it also provides the following additional services for users.

From the implementation of the ApplicationContext interface, we can see its characteristics: 1. Support for information sources that can be internationalized. (Implement MessageResource interface) 2. Access resources, (implement ResourcePatternResolver interface) 3. Support for application events. (implementation ApplicationEventPublisher)

1.2 BeanDefinition

The SpringIOC container manages the various Bean objects and their dependencies that we define. Bean objects are described in the Spring container as BeanDefinitions, which are Spring’s focus POJO objects. Their inheritance is as follows

1.3 BeanDefinitionReader

The Bean parsing process is very complex, and the functionality is very detailed, because there are a lot of things that are extended, so there must be enough flexibility


2.IOC container initialization

The initialization of the IOC container includes the process of locating, loading, and registering BeanDefinition resources. Let’s use ApplicationContext as an example. The ApplicationContext family of containers is probably the most familiar because the XmlWebApplicaitonContext that Web projects use belongs to this architecture, And ClasspathXMLApplicationContext, its inheritance system as shown in the figure below

ApplicationContext allows context nesting and maintains a context system by keeping the parent context. Bean look-ups can occur in this context architecture, first checking the current context, then the parent context, and thus providing a shared bean-definition environment for different Spring applications

First, let’s take a look at the entire flow of XmlBeanFactory. Using the source code of XmlBeanFactory, we can see that this class is actually obsolete, and its main flow is to maintain an XmlBeanDefinitionReader. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); , where this passes the Factory object

@Deprecated
@SuppressWarnings({"serial"."all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {

	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


	/**
	 * Create a new XmlBeanFactory with the given resource,
	 * which must be parsable using DOM.
	 * @param resource XML resource to load bean definitions from
	 * @throws BeansException in case of loading or parsing errors
	 */
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}

	/**
	 * Create a new XmlBeanFactory with the given input stream,
	 * which must be parsable using DOM.
	 * @param resource XML resource to load bean definitions from
	 * @param parentBeanFactory parent bean factory
	 * @throws BeansException in caseof loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }}Copy the code

Let’s try to implement the whole process programmatically

Next, we began to formal analysis of ApplicationContext initialization process, in a FileXmlApplicationContext entry analysis, we have divided into 20 small step

  • 1 to 7 locate resources
  • 8 to 15 for the load
  • 16 for registration

2.1 IOC anatomy of the rich and handsome

FileXmlApplicationContext provides multiple constructors, but in the end is the constructor is called

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {// To dynamically determine which loader to use to load our file super(parent); // Tell read config file where to put: position, to load configsetConfigLocations(configLocations); / / refreshif(refresh) { refresh(); }}Copy the code

2.2 Setting the Resource Loader and Locating resources

Through the analysis of the source code, can know of FileSystemXmlApplicationContext when create FileSystemXmlApplicationContext container constructors two important main job:

The first call to the superclass constructor super(parent) sets up the Bean resource loader for the container.

Then, call the superclass AbstractRefreshableConfigApplicationContext setConfigLocations method set bean definitions resource file location path

By tracking FileSystemXmlApplicationContext inheritance system, found that of its parent class parent AbstractApplicationContext initializes the IOC container in the main source is as follows:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext, DisposableBean {
    static {
		// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
		// on application shutdown inWebLogic 8.1. (Reported by Dustin Woods.) // To avoid class loading problems when WebLogic 8.1 is closed, Load the IOC container closing time (ContextClosedEvent class) ContextClosedEvent. Class. The getName (); } /** * Create a new AbstractApplicationContext with no parent. */ publicAbstractApplicationContext() {/ / parsing our resource file, the dynamic matching process enclosing resourcePatternResolver = getResourcePatternResolver (); } /** * Create a new AbstractApplicationContext with the given parent context. * FileSystemXmlApplicationContext calls the superclass constructor is called to change method * @ param parent the parent context * / public AbstractApplicationContext(ApplicationContext parent) { this();setParent(parent); } / * * * to get a Spring load of the Resource is used to read in Spring Bean definition Resource file * AbstractApplicationContext class inherited DefaultResourceLoader, so has the ability of Resource loading, This represents the DefaultResourceLoader object */ protected ResourcePatternResolvergetResourcePatternResolver() {
		returnnew PathMatchingResourcePatternResolver(this); }}Copy the code

AbstractApplicationContext PathMatchingResourcePatternResolver construction method in the constructor calls to create Spring resource loader.

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null"); // set the Spring resourceLoader this.resourceLoader = resourceLoader; }Copy the code
	String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
/**
	 * Set the config locations for this application context in init-param style,
	 * i.e. with distinct locations separated by commas, semicolons or whitespace.
	 * <p>If not set, the implementation may use a default as appropriate.
	 */
	public void setConfigLocation(String location) {// That is, between multiple files,; \t \n split, parsed into array formsetConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
	}

	/**
	 * Set the config locations for this application context.
	 * <p>If not setThe implementation may use a default as appropriate. The implementation bean defines the path to the resource file, handles multiple resource file string arrays */ public voidsetConfigLocations(String... locations) {
		if(locations ! = null) { Assert.noNullElements(locations,"Config locations must not be null");
			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

The source code for these two methods shows that we can either configure multiple Spring Bean definition resource files with a single string or use string arrays.

At this point, the Spring IOC container locates the configured Bean definition Resource file as a Spring-wrapped Resource during initialization

2.3 AbstractApplicationContext refresh function load Bean definition process

The Spring IOC container starts loading the Bean definition resource with the refresh method, which is a template method. The refresh() method does: Before the IOC container is created, if it already exists, it needs to be destroyed and closed to ensure that the newly created IOC container is used after refresh(). Refresh () acts like a reboot of the IOC container and initializes the container in the newly created container. To load Bean definitions resources FileXmlApplicationContext by calling the superclass AbstractApplicationContext refresh function start the IOC container loading process of Bean definitions

// The container initialization process reads in the Bean definition resource, @override public void refresh() throws BeansException IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this contextforRefreshing. // Call the container ready to refresh method to get the current time of the container, and set the synchronization flag prepareRefresh() to the container; // Tell the subclass to refresh the internal bean factory. Bean definitions of the resource files loaded starting from the subclass refreshBeanFactory method ConfigurableListableBeanFactory the beanFactory = obtainFreshBeanFactory (); // Prepare the bean factoryfor use in// prepareBeanFactory(BeanFactory) by configuring features such as class loaders, event handlers, etc. try { // Allows post-processing of the bean factoryin// Specify the special Beanpost event handler postProcessBeanFactory(beanFactory) for some subclasses of the container; // Invoke factory processors registered as beansinThe context. / / calls all registered spring BeanFactoryPostProcessor Bean invokeBeanFactoryPostProcessors (the beanFactory); // BeanPostProcessor is the bean's post-processor, so we can use the same processors as the bean's processors. Listens for container registerBeanPostProcessors trigger events (the beanFactory); // Initialize messagesource for// Initialize the information source, and internationalization related initMessageSource(); // Initialize event multicasterforThis context, / / initializes the container time spread initApplicationEventMulticaster (); // Initialize other special beansinSpecific context subclasses. // Call some special Bean initialization methods of subclasses onRefresh(); // CheckforListener beans and register them. // Register event listeners (); / / Instantiate all remaining (non - lazy - init) singletons. / / initialize all the rest of the singleton bean finishBeanFactoryInitialization (the beanFactory);  Corresponding event (); // The corresponding event () of the corresponding event () is not specified. } 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'CancelRefresh (ex); cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection cachesin Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); }}}Copy the code

The refresh method primarily provides for the declaration cycle management of IOC container beans. The Spring IOC container loads the Bean definition resource file from the refreshBeanFactory of its subcontainer, So the refresh () ConfigurableListableBeanFactory the beanFactory = obtainFreshBeanFactory (); The rest of this code registers the container’s sources and lifecycle events, and the loading process starts from this code.

2.4 Specific execution process of refreshBeanFactory method

AbstractApplicationContext obtainFreshBeanFactory () method call subclasses refreshBeanFactory method. To start the process of loading the Bean definition resource file by the container, the code looks like this:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// The template method pattern is used here. The parent class defines the abstract refreshBeanFactory method, which is implemented by the child class. ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ":" + beanFactory);
		}
		return beanFactory;
	}
Copy the code

AbstractApplicationContext class only abstract definition refreshBeanFactory () method, the container really call AbstractRefreshableApplicationContext implementation method, its source code is as follows

@Override
	protected final void refreshBeanFactory() throws BeansException {
		if(hasBeanFactory()) {// If a container already exists, destroy the beans in the container and turn off the container destroyBeans(); closeBeanFactory(); } try {/ / create the IOC container DefaultListableBeanFactory the beanFactory = createBeanFactory (); beanFactory.setSerializationId(getId()); // Customize the IOC container, such as setting startup parameters, enabling automatic assembly of annotations and other customizeBeanFactory(beanFactory); // Call the methods defined by the loaded Bean, using a template pattern 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

In this method, to determine whether the BeanFactory exists, if there is destroyed, and then create DefaultListableBeanFactory, and call the loadBeanDefinitions (the BeanFactory) in a bean definition.

2.5 AbstractRefreshableApplicationContext subclass loadBeanDefinitions method

AbstractRefreshableApplicationContext only defines the abstract loadBeanDefinitions method, container is really call subclasses AbstractXmlApplicationContext for implementation of this method, AbstractXmlApplicationContext main source is as follows:

    @Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for// Create XmlBeanDefinitionReader, which is to create Bean readers and set them to containers via callbacks. The container uses this reader to read XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); // Spring resource loader for Bean reader location, AbstractRefreshableApplicationContext / / ancestor of the parent class DefaultResourceReader AbstractRefreshableApplicationContext inheritance, therefore, The container itself is a resource beanDefinitionReader. SetResourceLoader (this); / / for Bean reader Settings SAX XML parser beanDefinitionReader. SetEntityResolver (new ResourceEntityResolver (this)); // Allow a subclass to provide custom initialization of the reader, // Then proceed with actually loading the bean definitions. Enable Xml validation mechanism initBeanDefinitionReader(beanDefinitionReader); // Bean reader loadBeanDefinitions(beanDefinitionReader); }Copy the code
Protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {/ / azimuth and bean definitions resources / / here using the template pattern, called subclass methods to obtain bean as resources / / this method is implemented in the ClassPathXmlApplicationContext, For us / / sample FileXmlApplicationContext without using this method the Resource [] configResources = getConfigResources ();if(configResources ! = null) {/ / XML Bean reader calls its parent class Bean definitions about the location of AbstractBeanDefinitionReader read reader resources. LoadBeanDefinitions (configResources); } / / if a subclass of Bean locate resources is empty, obtain FileXmlApplicationContext construction methodsetString[] ConfigLocations = getConfigLocations();if(configLocations ! = null) {/ / Xml Bean reader calls its parent class AbstractBeanDefinitionReader reader read the positioning of the Bean definition resources. LoadBeanDefinitions (configLocations); }}Copy the code

In the abstract superclass AbstractBeanDefinitionReader defines the loading process

/** * override the parent method, Call loadBeanDefinitions(String location, Set<Resource> actualResources) method */ @override public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {return loadBeanDefinitions(location, null);
	}

	/**
	 * Load bean definitions from the specified resource location.
	 * <p>The location can also be a location pattern, provided that the
	 * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
	 * @param location the resource location, to be loaded with the ResourceLoader
	 * (or ResourcePatternResolver) of this bean definition reader
	 * @param actualResources a Set to be filled with the actual Resource objects
	 * that have been resolved during the loading process. May be {@code null}
	 * to indicate that the caller is not interested in those Resource objects.
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #getResourceLoader()
	 * @see #loadBeanDefinitions(org.springframework.core.io.Resource)
	 * @see #loadBeanDefinitions(org.springframework.core.io.Resource[])*/ public int loadBeanDefinitions(String location, Set the < Resource > actualResources) throws BeanDefinitionStoreException {/ / in the process of the IOC container initialization setting the Resource loaders, See Step 2.2 // Loading multiple Bean definition resource files at specified locations ResourceLoader ResourceLoader = getResourceLoader();if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if(resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources  = ((ResourcePatternResolver) resourceLoader).getResources(location); // Delegate loads using the XmlBeanDefinitionReader method of its subclass 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 {
			// Can only load single resources by absolute URL.
			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 + "]");
			}
			return loadCount;
		}
	}

	@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int counter = 0;
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}
Copy the code

loadBeanDefinitions(Resource… Resources, a loadBeanDefinitions method that calls XmlBeanDefinitionReader, is similar to the three methods analyzed last year.

From analyzing AbstractBeanDefinitionReader loadBeanDefinitions method source code can be seen that the method to do two things First of all, Call resource loaders for resources method resourceLoader. GetResource (location); Gets the resource to load. Second, the loadBeanDefinitions method of its subclass XmlBeanDefinitionReader actually performs the loading function

2.6 The resource loader obtains the resource to be read

AbstractBeanDefinitionReader by calling DefaultResourceLoader getResource (location); Method to get the resource to load, whose source is shown below

*/ @override public Resource getResource(String location) {assert. null (location,"Location must not be null");

		for (ProtocolResolver protocolResolver : this.protocolResolvers) {
			Resource resource = protocolResolver.resolve(location, this);
			if(resource ! = null) {returnresource; }}if (location.startsWith("/")) {
			returngetResourceByPath(location); } // In the case of classpath (classpath:xx), you need to use the ClassPathResource to get the resource object of the bean fileelse if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
		}
		else{ try { // Try to parse the location as a URL... // Use UrlResource as resource object URL of bean file URL = new URL(location);returnnew UrlResource(url); } catch (MalformedURLException ex) {// No URL -> resolve as resource path. // call the container's own getResourceByPath method to get the ResourcereturngetResourceByPath(location); }}}Copy the code

FileSystemXmlApplicationContext container provides getResourceByPath method, in order to deal with is neither a classpath identifier, and not the URL identifies the Resource location this situation

@Override
	protected Resource getResourceByPath(String path) {
		if(path ! = null && path.startsWith("/")) { path = path.substring(1); } // This uses file system resource objects to define bean filesreturn new FileSystemResource(path);
	}
Copy the code

This code is returned to the FileSystemXmlApplicationContext, he offers from FileSystemResource to complete configuration file from file system resource definitions

In this way, the IOC configuration file can be loaded from the file path – of course we can follow this logic from anywhere, and in Spring we see the various resource abstractions it provides, Such as ClassPathResource URLResource, FileSystemResource etc for our use. What we have seen above is a process for locating resources, and this is only part of the loading process

At this point, we have completed the location of IOC resource files

2.8 XmlBeanDefinitionReader loads Bean definition resources

With the Resource defined for the Bean, go back to loadBeanDefinitions(Resource…) in XmlBeanDefinitionReader. Method to see the loading process after the resource definition representing the bean file

/** * XmlBeanDefinitionReader Throws XmlBeanDefinitionReader */ @override public int loadBeanDefinitions(Resource Resource) throws BeanDefinitionStoreException {/ / special encoding processing will read in an Xml resourcereturn loadBeanDefinitions(new EncodedResource(resource));
	}

	/**
	 * Load bean definitions from the specified XML file.
	 * @param encodedResource the resource descriptor for the XML file,
	 * allowing to specify an encoding to use for parsing the file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}

		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if(! currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } the try {/ / convert resource file to Input the IO stream InputStream InputStream. = encodedResource getResource () getInputStream (); InputSource = new InputSource(inputStream); InputSource = new InputSource(inputStream);if(encodedResource.getEncoding() ! = null) { inputSource.setEncoding(encodedResource.getEncoding()); } // Here is the specific reading processreturn doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
            // 关闭从Resource中得到的IO流
				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 method that actually loads the bean-definition resource from a specific XML file */ protected intdoLoadBeanDefinitions(InputSource inputSource, The Resource the Resource) throws BeanDefinitionStoreException {try {/ / XML Document into a DOM object, the parsing process are done by documentReader Document doc =doLoadDocument(inputSource, resource); // Here is the detailed process for starting the Bean definition resource, which uses Spring's Bean configuration rulesreturn registerBeanDefinitions(doc, resource);
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (SAXParseException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
		}
		catch (SAXException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"XML document from " + resource + " is invalid", ex);
		}
		catch (ParserConfigurationException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Parser configuration exception parsing XML from " + resource, ex);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"IOException parsing XML document from " + resource, ex);
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Unexpected exception parsing XML document from "+ resource, ex); }}Copy the code

2.9 DocumentReader Converts bean definition resources into Document objects

The source code for DocumentReader to convert the Bean definition resource into a Document object is shown below

/** * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured * XML parser. * */ @override Public document loadDocument(InputSource InputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, Boolean namespaceAware) throws Exception {// Create a file parser factory DocumentBuilderFactory = createDocumentBuilderFactory(validationMode, namespaceAware);if (logger.isDebugEnabled()) {
			logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } // createDocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource);
	}
Copy the code
	/**
	 * Create the {@link DocumentBuilderFactory} instance.
	 * @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD}
	 * or {@link XmlValidationModeDetector#VALIDATION_XSD XSD})
	 * @param namespaceAware whether the returned factory is to provide support for XML namespaces
	 * @return the JAXP DocumentBuilderFactory
	 * @throws ParserConfigurationException ifwe failed to build a proper DocumentBuilderFactory */ protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, Boolean namespaceAware) throws a ParserConfigurationException {/ / create a file parser factory DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(namespaceAware); // Set the validation for parsing XMLif(validationMode ! = XmlValidationModeDetector.VALIDATION_NONE) { factory.setValidating(true);
			if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
				// Enforce namespace aware for XSD...
				factory.setNamespaceAware(true);
				try {
					factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
				}
				catch (IllegalArgumentException ex) {
					ParserConfigurationException pcex = new ParserConfigurationException(
							"Unable to validate using XSD: Your JAXP provider [" + factory +
							"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
							"Upgrade to Apache Xerces (or Java 1.5) for full XSD support"); pcex.initCause(ex); throw pcex; }}}return factory;
	}
Copy the code

This parsing procedure calls the JAXP standard of JavaEE for processing.

At this point, the Spring IOC container completes the process of loading, reading and converting the Bean resource file to a Document object according to its location.

Next, we’ll look at how the Spring IOC container converts the loaded Bean definition resource file into a Document to parse it into a Spring IOC-managed Bean object and register it with the container

2.10 XmlBeanDefinitionReader parses loaded Bean definition resource files

The doLoadBeanDefinitions method in the XmlBeanDefinitionReader class is the method that actually loads bean-definition resources from a particular XML file. After loading the bean-definition resources, this method converts them into a Document object, Next, call registerBeanDefinitions to initiate the Spring IOC container’s parsing of the Bean definition. The registerBeanDefinitions source code is shown below

	/**
	 * Register the bean definitions contained inThe given DOM Document. * Installing Spring's Bean definition requires that the Bean definition resource be parsed into an internal container data structure * Called by {@code loadBeanDefinitions}. * <p>Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * @param doc the DOM document * @param resource the resource descriptor (for context information)
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of parsing errors
	 * @see #loadBeanDefinitions
	 * @see #setDocumentReaderClass
	 * @see BeanDefinitionDocumentReader#registerBeanDefinitions*/ public int registerBeanDefinitions(Document doc, The Resource the Resource) throws BeanDefinitionStoreException {/ / get BeanDefinitionDocumentReader to parse the XML format beanDefinition format BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); Int countBefore = getRegistry().getBeandefinitionCount (); // Parse the process entry, where the prototype pattern is used, BeanDefinitionDocumentReader just an interface / / specific parsing process completed by implementing class DefaultBeanDefinitionDocumentReader documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // Count the number of beans parsedreturn getRegistry().getBeanDefinitionCount() - countBefore;
	}
Copy the code

The loading and parsing of Bean definition resources is mainly divided into the following two processes: 1. First, the XML parser is called to convert the Bean definition resource files into Document objects, but these objects are not parsed according to Spring’s Bean rules, this step is the loading process 2. Second, after the generic XML parsing, the Document object is parsed according to Spring’s bean rules

According to the Spring bean rules for the process Document object is the realization of the interface BeanDefinitionDocumentReader class DefaultBeanDefinitionDocumentReader

2.11 DefaultBeanDefinitionDocumentReaderDocument object resolution for bean definition:

BeanDefinitionDocumentReader interface by calling its implementation class DefaultBeanDefinitionDocumentReader to parse the Document object, parsing the code below

/* Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {// Get the XML descriptor this.readerContext = readerContext; logger.debug("Loading bean definitions"); // Get the Document root Element Element root = doc.getDocumentElement(); / / calldoRegisterBeanDefinitions implements the specific parsing processdoRegisterBeanDefinitions(root);
	}
	/**
	 * Register each bean definition within the given root {@code <beans/>} element.
	 */
	protected void doRegisterBeanDefinitions(Element root) {
		// 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,
		// thenultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates Without later necessitating one. / / the specific parsing process by BeanDefinitionParserDelegate implementation, Here with the delegate pattern / / BeanDefinitionParserDelegate defines various elements of Spring definition XML file BeanDefinitionParserDelegate parent = this. Delegate; this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if(! getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isInfoEnabled()) {
						logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return; PreProcessXml (root); preProcessXml(root); // parseBeanDefinitions(root, this.delegate); // parseBeanDefinitions(root, this.delegate); PostProcessXml (root); postProcessXml(root); // Prepare for recursive parsing of child elements this.delegate = parent; Objects created BeanDefinitionParserDelegate} / * * *, For true parsing process * / protected BeanDefinitionParserDelegate createDelegate (XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) { BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); / / from BeanDefinitionParserDelegate initialize the Document root element delegate. InitDefaults (root, parentDelegate);return delegate;
	}
Copy the code
	/**
	 * Parse the elements at the root level in the document:
	 * "import"."alias"."bean". * @param root the DOM root element of the document * parsing the Bean's document object from the root of the document using Spring's Bean definition rules */ protected Void parseBeanDefinitions (Element root, BeanDefinitionParserDelegate delegate) {/ / Bean definition Document object USES the Spring the default XML namespaceif(delegate. IsDefaultNamespace (root)) {/ / from the Bean definition Document object get all the child nodes of the root element NodeList nl = root. GetChildNodes ();for(int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); // Get Document node is XML element nodeif(node instanceof Element) { Element ele = (Element) node; // The element nodes of the Document defined by the Bean use Spring's default XML namespaceif(delegate. IsDefaultNamespace (ele)) {/ / use the Spring bean analytic element node parseDefaultElement (ele, delegate); }else{/ / Spring the default XML namespace is not used, use the parsing rules of user custom analytic element node delegate. ParseCustomElement (ele); }}}}else{/ / Spring the default XML namespace is not used, use the parsing rules of user custom analytic element node delegate. ParseCustomElement (root); }}Copy the code
/** * Private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {/ / if the element node is < Import > Import element, to Import the parsingif(delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } // If the element node is <Alias> import elements, import parsingelse if(delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } // The element node is neither an imported nor an alias element, i.e. a normal <Bean> element. // Spring's Bean rules parse the elementelse if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele); }}Copy the code
	/**
	 * Parse an "import"Element and load the bean definitions * from the given resource into the bean factory. From the given import path to load Bean definitions resources to the Spring IOC container * / protected void importBeanDefinitionResource Element (ele) {/ / for a given location attribute of the import Element String location = ele.getAttribute(RESOURCE_ATTRIBUTE); // If the location attribute of the imported element is empty, no value is imported and returns directlyif(! StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele);
			return;
		}

		// Resolve system properties: e.g. "${user.dir}"/ / use system variable values parsed the location attribute value location = getReaderContext () getEnvironment () resolveRequiredPlaceholders (location); Set<Resource> actualResources = new LinkedHashSet<Resource>(4); // Discover whether the location is an absolute or relative URI // Indicate whether the location of a given imported element is an absolute or relative URI. Boolean absoluteLocation =false;
		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
			// cannot convert to an URI, considering the location relative
			// unless it is the well-known Spring prefix "classpath*:"} // Absolute or relative? // The location of the given imported element is the absolute pathif(absoluteLocation) {try {// Use the resource reader loader to load the bean definition resource of the given path, 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// No URL -> considering resource location as relative to the current file.try {int importCount; // encapsulate the location of the given imported element as a relativeResource path Resource relativeResource = getReaderContext().getresource ().createrelative (location); // The relative path of encapsulation existsif(relativeResource.exists()) {// Use the resource read loader to load the bean definition resource for the given path. ImportCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } // The relative path of the encapsulation does not existelseString 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); } } Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]); GetReaderContext ().fireImportProcessed(location, actResArray, extractSource(ele)); // After parsing the <Import> element, send the container to Import other resources. }Copy the code
	/**
	 * Process the given alias element, registering the aliasWith the registry. * Resolve the <Alias> Alias element, Register the Bean alias */ protected void processAliasRegistration(Element ele) {String name = ele.getAttribute(NAME_ATTRIBUTE); Stringalias = ele.getAttribute(ALIAS_ATTRIBUTE);
		boolean valid = true;
		if(! StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele);
			valid = false;
		}
		if(! StringUtils.hasText(alias)) {
			getReaderContext().error("Alias must not be empty", ele);
			valid = false;
		}
		if (valid) {
			try {
				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 we can see from the above Document parsing of the loaded Bean definition by the Spring IOC container, when we use Spring, we can use elements in the Spring configuration file to import other resources needed by the IOC container. The Spring IOC container first specifies the imported resources to be loaded into the container when it is parsed. When using an alias, the Spring IOC container first registers the alias defined by the alias element with the container.

For neither nor elements, namely the common elements in the Spring configuration file parsing by BeanDefinitionParserDelegate parseBeanDefinitionElement method to implement the class.

2.12 BeanDefinitionParserDelegate parsing define the elements of a resource file

For neither nor elements, namely the common elements in the Spring configuration file parsing by BeanDefinitionParserDelegate parseBeanDefinitionElement method to implement the class. The source code is as follows:

/** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {/ / BeanDefinitionHolder encapsulation of BeanDefinition Or Bean definitions encapsulation / / < Bean > element analytical by BeanDefinitionParserDelegate BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if(bdHolder ! = null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); Try {// Register the final Decorated instance. // Register the resolved Bean definition with the Spring IOC container, Is this Bean definition to the IOC container registration entry 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(new BeanComponentDefinition(bdHolder)); }}Copy the code
/ / parsing < Bean > Element entry public BeanDefinitionHolder parseBeanDefinitionElement Element (ele) {returnparseBeanDefinitionElement(ele, null); } // Parse <Bean> elements, this method mainly deals with ids, The name and alias attribute public BeanDefinitionHolder parseBeanDefinitionElement (Element ele. BeanDefinition containingBean) { String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); // Store all the name attribute values in the <Bean> element in the aliasif(StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; // If there is no ID in the <Bean> element, the first element in the alias is copied to beanNameif(! 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"); }} // Check the uniqueness of the id and name in the <Bean> element. ContainingBean indicates whether the <Bean> element contains child <Bean> elementsif(containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } / / details for the < Bean > element in the configuration of Bean definitions where parsing AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement (ele, beanName. containingBean);if(beanDefinition ! = null) {if(! Stringutls.hastext (beanName)) {try {// If there is no id, name, or alias configured in the <Bean> element, and no child elements are contained, a unique beanName is generated and registered for the parsed Beanif(containingBean ! = null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(),true);
					}
					else{/ / if you have id or name, or don't name elements, use the sound to register beanName = this. ReaderContext. GenerateBeanName (beanDefinition); // Register analias for the plain bean class name, if still possible,
						// if the generator returned the class name plus a suffix.
						// This is expected forSpring 1.2/2.0 backwards compatibility. // Spring 1.2/2.0 backwards compatibility. // Spring 1.2/2.0 backwards compatibility 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
	/**
	 * Parse the bean definition itself, without regard to name or aliases. May return
	 * {@code null} ifProblems occurred during the parsing of the bean definition. * Parsing the < bean > element occurred during parsing of the bean definition. The child elements are parsed, Here only for other elements parsing * / public AbstractBeanDefinition parseBeanDefinitionElement (Element ele, String beanName, BeanDefinition containingBean) {this.parsestate.push (new BeanEntry(beanName)); String className = null; String className = null; String className = null;if(ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try {// If the <Bean> element has a parent value, get the parent property value String parent = null;if(ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } // Create BeanDefinition from <Bean> element config class name and parent attribute, AbstractBeanDefinition bd = createBeanDefinition(className, parent); / / for the current < Bean > element in the configuration of some attributes parsing and configuration, such as single case attributes parseBeanDefinitionAttributes (ele, beanName containingBean, bd); / / analytical Description attribute bd. SetDescription (DomUtils. GetChildElementValueByTagName (ele, DESCRIPTION_ELEMENT)); ParseMetaElements (ele, bd); / / methods parseLookupOverrideSubElements lookup - method (ele, bd. GetMethodOverrides ()); / / methods parseReplacedMethodSubElements replaced - method (ele, bd. GetMethodOverrides ()); / / analytical method to construct parseConstructorArgElements (ele, bd); // Parse <Propterty> property parsePropertyElements(ele, bd); <Qualifier> 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

Anyone who has used Spring and is familiar with Spring configuration files will know from analyzing the source code above that the properties we configure in the elements of the Spring configuration file are parsed and set into the Bean using this method

Note: This is just putting information resolution into the BeanDefinition, not instantiation, which happens when the client first asks the container for the Bean in dependency injection

So let’s move on. Know how bean properties are set

2.13 BeanDefinitionParserDelegate analytic properties

BeanDefinitionParserDelegate elements in the analytical method is invoked parsePropertyElements child elements, parsing the source code is as follows:

	public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
		NodeList nl = beanEle.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
				parsePropertyElement((Element) node, bd);
			}
		}
	}

	/**
	 * Parse a property element.
	 */
	public void parsePropertyElement(Element ele, BeanDefinition bd) {
		String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
		if(! StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele);
			return; } this.parseState.push(new PropertyEntry(propertyName)); Try {// If a property with the same name has already been parsed, the parse is not repeated // that is, if a property with the same name is configured in a Bean, only the first property is takenif (bd.getPropertyValues().contains(propertyName)) {
				error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
				return; } Object = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); ParseMetaElements (ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); }}Copy the code
	/**
	 * Get the value of a property element. May be a list etc.
	 * Also used for constructor arguments, "propertyName" being null inthis case. */ public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName ! = null) ?"<property> element for property '" + propertyName + "'" :
						"<constructor-arg> element"; // Should only have one child element: Ref, value, list, etc. // Get all child elements of one type only: ref, value, list etc. NodeList nl = el.getChildNodes (); Element subElement = null;for(int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); // Child elements are not description and meta attributesif(node instanceof Element && ! nodeNameEquals(node, DESCRIPTION_ELEMENT) && ! nodeNameEquals(node, META_ELEMENT)) { // Child element is what we're looking for. if (subElement ! = null) { error(elementName + " must not contain more than one sub-element", ele); } else { subElement = (Element) node; } } } boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement ! = null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } // If the attribute is ref, Create a ref data object RuntimeBeanReference that encapsulates the ref information if (hasRefAttribute) {String refName = ele.getAttribute(REF_ATTRIBUTE); if (! StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } else if (hasValueAttribute) {TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } else if (subElement ! = null) { return parsePropertySubElement(subElement, bd); } else { // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); return null; }}Copy the code

By analyzing the above source code, we can see how the configuration of elements within elements is handled in the Spring configuration file

  1. Ref is encapsulated as a reference to an object. 2.value is encapsulated as an object of type string. 3.ref and value both associate the attribute value/reference with the referenced attribute via “parsed data type value”.setsource ()

The method finally parsePropertySubElement method parsePropertySubElement method parsePropertySubElement method, we continue to analyze the source code

2.15 Parsed child elements

	public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
		return parsePropertySubElement(ele, bd, null);
	}

	/**
	 * Parse a value, ref or collection sub-element of a property or
	 * constructor-arg element.
	 * @param ele subelement of property element; we don't know which yet * @param defaultValueType the default type (class name) for any * {@code 
      
       } tag that might be created */ public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { if (! isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd ! = null) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } else if (nodeNameEquals(ele, REF_ELEMENT)) { // A generic reference to any name of any bean. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (! StringUtils.hasLength(refName)) { // A reference to the id of another bean in the same XML file. refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); if (! StringUtils.hasLength(refName)) { // A reference to the id of another bean in a parent context. refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (! StringUtils.hasLength(refName)) { error("'
      bean', 'local' or 'parent' is required for 
      
        element", ele); return null; } } } if (! StringUtils.hasText(refName)) { error("
       
         element contains empty target attribute", ele); return null; } RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); ref.setSource(extractSource(ele)); return ref; } else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } else if (nodeNameEquals(ele, NULL_ELEMENT)) { // It'
       
      s a distinguished null value. Let's wrap it in a TypedStringValue // object in order to preserve the source location. TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; }}Copy the code

Through the above source code analysis, we understand that in the Spring configuration file, the list, array, set, map, prop and other seed element sets configured in the element are parsed through the above method to generate corresponding data objects

After layer upon layer parsing of the elements in the Document object of the Spring Bean definition resource file transformation, Spring IOC has now converted the Bean definition resource file defined in XML form into the data structure recognized by Spring IOC –BeanDefinition, It is the mapping of the POJO object of the Bean definition resource file configuration in the Spring IOC container. AbstractBeanDefinition can be used as an entry point to see the IOC container indexing, querying, and manipulating

After parsing the Bean definition resource file by the Spring IOC container, the IOC container is mostly done with managing the Bean objects, the initialization process, but the most important dependency injection has yet to occur. Currently, beanDefinitions in the IOC container only store static information, and the Bean definition information needs to be registered with the container to complete the initialization process of the IOC container

2.16 Registration of parsed BeanDefinition in IOC container:

Let’s continue to follow up the implementation of the program order, then we went back to 2.12 DefaultBeanDefinitionDocumentReader transformation to the Bean definition Document object analytic process, The BeanDefinition object obtained after parsing the Document object in its pareBeanDefaultElement method, Then call BeanDefinitionUtils registerBeanDefinition to register the parsed bean with the IOC container.

/** * Register the given bean definition with the given bean factory. * @param registers the given bean to the given beanFactory definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with * @throws BeanDefinitionStoreExceptionifregistration failed */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); // BeanDefinitionRegistry's registerBeanDefinition method is used Pay attention to the FileSystemXmlBeanFactory BeanDefinitionRegistry here is, in fact, we, / / because of its inherited DefaultListableBeanFactory, And then implements the BeanDefinitionRegistry interface registry. RegisterBeanDefinition (beanName definitionHolder. GetBeanDefinition ()); // Register aliasesfor bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if(aliases ! = null) {for (String alias : aliases) {
				registry.registerAlias(beanName, alias); }}}Copy the code
	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null"); // Validates parsed beansif (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition oldBeanDefinition;


		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		if(oldBeanDefinition ! = null) {// Checks if a bean with the same name is already registered, and throws an exception if it is, and the registered bean is not allowed to be overwrittenif(! isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound."); } // If the registered bean is allowed to be overwritten, the new bean overwrites the old beanelse if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]"); }}else if(! beanDefinition.equals(oldBeanDefinition)) {if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]"); }}else {
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } // If there is no already registered bean with the same name, register as normalelse {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if(this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; }}}else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if(oldBeanDefinition ! = null | | containsSingleton (beanName)) {/ / reset all registered bean resetBeanDefinition (beanName); }}Copy the code

As you can see from the registered code, the IOC container actually maintains a ConcurrentHashMap to hold the bean’s definition information, where key is the bean’s name and value is a BeanDefinition object.

At this point, the beans configured in the Bean definition resource file have been parsed and registered with the IOC container to be managed by the container. Now that the entire Bean configuration information has been established in the IOC container, the BeanDefinition information is available and can be retrieved. The role of the IOC container is to register and maintain the registered Bean definition information. This registered bean definition information is the basis for the INVERSION of control of the IOC container, and it is with this registered data that the container can perform dependency injection


3 summary

Through the above code analysis, the basic steps for IOC container initialization are summarized:

1. The entry to initialization is done in refresh(), which is implemented in the container. 2. The IOC container loaded with bean definitions uses a loadBeanDefinitions method.

The general process is: through ResourceLoader to complete the location of the resource file, DefaultResourceLoader is the default implementation, and the context itself gives the implementation of ResourceLoader, can be from the classpath, file system, URL and other ways to locate resources. If XmlBeanFactory is an IOC container, then you need to specify bean-defined resources for it. That is, bean definition files are abstracted as resources to be processed by the IOC container. BeanDefinitionReader is used by the container to parse and register definition information. Often use XmlBeanDefinitionReader to parse the XML files bean definition file – the actual process is entrusted to BeanDefinitionParserDelegate to complete, so as to get the bean definition of information, This information is represented in Spring using a BeanDefinition object — the name reminds us of loadBeanDefinition, registerBeanDefinition, and other related methods that handle BeanDefinition services, After the container is resolved to BeanDefinition, it needs to be registered in the IOC container, which is realized by the IOC implementation BeanDefinitionRegistry interface. The registration process is the process of maintaining a HashMap inside the IOC container to store the BeanDefinition. This HashMap is where the IOC container holds the bean information, and all subsequent bean operations are done around this HashMap

3. Then we can use the BeanFactory and ApplicationContext to enjoy SpringIOC services. When working with the IOC container, we noticed that aside from a little pasting code, the vast majority of application code written in the correct IOC style was done without caring how to get to the factory, because the container would hook these objects up with other container-managed objects. The basic strategy is to put the factory where it is known, preferably where the expected headers make sense, and where the code will actually access the factory. Spring itself implements the framework for declaratively loading the application context of a Web application and storing it in a ServletContext.

We also need to distinguish between two concepts when using SpringI0C containers :BeanFactory and FactoryBean

Including the BeanFactory programming abstraction refers to the IOC container, such as ApplicationConitext XmlBeanFactory, etc., these are the manifestation of the IOC container, you need to use what kind of container is determined by the customer, but Spring provides rich choice for us. A FactoryBean is just a bean that can be managed in an IOC container. It is an abstraction used for various processes and resources. A FactoryBean generates another object when needed, rather than returning the FactoryBean itself. The call to it returns the product produced by the factory. All FactoryBean special org. Springframework. Beans. Factory. FactoryBean interface, when using the FactoryBean container, the container will not return to the FactoryBean. Itself, but returns the object it generated. Spring includes most generic resources and service access to abstract FactoryBeans. For JNDI lookup, proxy object, transactional proxy, RMI proxy, etc., we can think of these as concrete factories that Spring has set up for us. That is, Spring uses the abstract factory pattern to prepare a series of factories for us to produce specific objects, eliminating the need for manual rework, and simply configuring them in the IOC container for easy use

Finally, summarize a method call diagram, temporarily ugly, followed by a regular method call diagram