Then the content of the section in the previous chapter, we to analyze when new a FileSystemXmlApplicationContext object, spring what happened exactly. FileSystemXmlApplicationContext class content is mainly defines several overloaded constructors, core construction method is as follows:
/** * Create a new FileSystemXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * * loading all bean definitions and creating all singletons. * Alternatively, call refresh manually after further configuring the context. * */ public FileSystemXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); }}Copy the code
As you can see from the method description, all bean definitions are loaded in this constructor and bean singleton instances are created. The refresh () method is the entrance to the IOC container is initialized, the refresh () method is a AbstractApplicationContext class, this is an abstract class, it implements the ApplicationContext basis function, using the template method pattern here, Provides a uniform template for subclasses that implement it:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. Tell subclass refresh internal bean factory ConfigurableListableBeanFactory the 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
The refresh() method lists the steps for initializing the IOC container. The first method is initialization preparation, which simply sets the start date and activity identifier and performs the initialization of the property source. We focus on the second method obtainFreshBeanFactory (), which tells the subclass refresh internal bean factory, return a ConfigurableListableBeanFactory, tracking the method:
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
/**
* Return the internal bean factory of this application context.
* Can be used to access specific functionality of the underlying factory.
*
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
Copy the code
The first line of the obtainFreshBeanFactory() method calls refreshBeanFactory(), which is an abstract method implemented by its subclasses, and the second line of the method calls getBeanFactory(), which is an empty method defined in its parent interface. Abstract methods refreshBeanFactory () in its subclass subclasses AbstractRefreshableApplicationContext implementation:
/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the Context's lifecycle. * * This implementation performs an actual refresh of the underlying bean factory for that context, closing the previous bean factory (if any), * And initialize a new bean factory for the next phase of the context life cycle */ @override protected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); 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 method is modified with the final keyword, meaning that it cannot be overridden, and initialization of the IOC container is done in this method. The first step to determine any existing factories, anything is destroyed, and then create a default factory, namely DefaultListableBeanFactory, over the next two lines of code is set some properties of the bean factory, Note the loadBeanDefinitions(beanFactory) line. After creating a default bean factory, load the bean definition, similar to the original way we initialized the bean factory in the previous chapter. It is not hard to see from here, a constructor FileSystemXmlApplicationContext is already contains the section in the previous chapter we original initialization process. Next, we track the loadBeanDefinitions (the beanFactory) implementation, the method is by AbstractXmlApplicationContext class implements the abstract:
/** * Loads the bean definitions via an XmlBeanDefinitionReader. To load bean definitions by XmlBeanDefinitionReader * * / @ Override protected void loadBeanDefinitions (DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 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
The first line of the method defines a Reader, which is used to read the XML configuration file, and the last line is the actual implementation of loading the bean definition as follows:
/** * Load the bean definitions with the given XmlBeanDefinitionReader. * */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources ! = null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations ! = null) { reader.loadBeanDefinitions(configLocations); }}Copy the code
The above method calls the loadBeanDefinitions(EncodedResource EncodedResource) method of the XmlBeanDefinitionReader class:
/** * Load bean definitions from the specified XML file. * rows 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<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (! currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!" ); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() ! = null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); }}}Copy the code
As you can see from the method description, this method loads the bean definition from the specified XML file, and the code in the try block is the process of loading the bean definition. Spring wraps the input stream returned by the resource and passes it to the doLoadBeanDefinitions() method, which we enter as follows:
/**
* Actually load bean definitions from the specified XML file.
*
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return 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);
}
}
/**
* Actually load the specified document using the configured DocumentLoader.
*
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
Copy the code
As can be seen from the code in the try block, Spring uses the documentLoader to convert the resource into the Document resource. The documentLoader used by Spring is DefaultDocumentLoader. The loadDocument method is defined in this class:
/** * 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 { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); }Copy the code
From here, you can see that this is the familiar DOM parsing XML. You can imagine That Spring parses the nodes and attributes of the XML file according to the format specified by the XSD file. Back to the registerBeanDefinitions(Doc, Resource) method,
/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
*
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
Copy the code
The method specification explicitly tells us that this method registers the bean definition contained in the given DOM document. Spring parses the wrapped input stream into a DOM document, and then registers the bean definition information contained in the DOM into the Map<String, BeanDefinition> object held by the IOC container. As long as our IOC container holds the bean definition, we can properly produce bean instances.
By reading the source code, we analyzed the implementation principle of Spring IOC. Some implementation details are not delved into, but it is more important to understand its core ideas and implementation ideas.
Three things to watch ❤️
If you find this article helpful, I’d like to invite you to do three small favors for me:
-
Like, forward, have your “like and comment”, is the motivation of my creation.
-
Follow the public account “Java rotten pigskin” and share original knowledge from time to time.
-
Also look forward to the follow-up article ing🚀
-
[666] Scan the code to obtain the learning materials package
Author: Wind program ape
Source: www.cnblogs.com/fangfuhai/p…