Spring IoC’s container architecture
IoC container is the core module of Spring, which abstracts object management and dependency management framework solution. Spring provides a number of containers, of which the BeanFactory is the top-level (root) container and cannot be instantiated. It defines a set of principles that all IoC containers must follow. Specific container implementations can add additional functionality, such as the common ApplicationContext. Its more specific implementation such as ClassPathXmlApplicationContext contains content, parse the XML and a series of AnnotationConfifigApplicationContext is includes annotations analytic and so on a series of content. The Spring IoC container inheritance system is smart enough to use whichever layer you need, rather than the full-featured one.
The BeanFactory top-level interface method stack is as follows
The BeanFactory container inheritance is as follows
From its interface design, we can see that the ApplicationContext we always use inherits the BeanFactory sub-interface, ResourceLoader, MessageSource, etc., so it provides richer functionality.
Below we ClasspathXmlApplicationContext, for example, deep source of IoC container initialization process.
BeanFactory creation process
Get the BeanFactory subprocess
Entry: org. Springframework. Context. Support. AbstractApplicationContext# refresh
/** * This method does the IOC container creation and initialization work * the most important steps in this method are the second and eleventh steps ** /
@Override
public void refresh(a) throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// STEP 1: refresh preprocessing
// Preparations include setting the startup time, whether to activate the identity bit, and initializing the property source configuration
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/ / use DefaultListableBeanFactory is DefaultListableBeanFactory subclass
// Initialize the bean factory
/ / STEP 2:
/ / a) to create the IoC container (DefaultListableBeanFactory)
// b) load the parsed XML file (eventually stored in the Document object)
// c) read the Document object and complete the loading and registration of BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// The most important method, prepare the bean factory
// STEP 3: Preprocess the IoC container (set some common attributes)
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// This method does not have any code in the current version of Spring, perhaps spring expects it to be extended in a later version
// Empty shell method
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// More important method
// Execute the registered Factory Processors in spring's environment
/ / set custom ProcessorFactory and defined within a spring (ConfigutationClassPoetProcessor)
/ / spring BeanFactoryPostProcessor ConfigurationClassPostProcessor is maintained within a spring
/ / the following method in the main executive ConfigurationClassPostProcessor method
// STEP 5: Call BeanFactoryPostProcessor to process BeanDefinition
/** * BeanFactoryPostProcessor is one of spring's extension points. Spring allows BeanFactoryPostProcessor to read its configured metadata before the container instantiates any other beans * and can modify it as needed, For example, you can change the scope of the bean from Singleton to Prototype. You can also change the value of the property to * You can configure multiple spring BeanFactoryPostProcessor at the same time, and by setting the 'order' properties to control each spring BeanFactoryPostProcessor * BeanFactoryPostProcessor is executed after the Bean definition file is loaded by the Spring container and before the bean is instantiated
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// The previous line of code already put some of the post-processors into bdMap, including the custom BeanPostProcessor
// Register the BeanPostProcessor
// Remove all the post-processors in bdMap,
// Add some other post-processors to the factory list
// STEP 6: Register the BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// Not important, internationalization processing
// STEP 7: Initialize some message sources (such as i18N for internationalization)
initMessageSource();
// Initialize event multicaster for this context.
// It is not important to handle the event
// STEP 8: Initializes the application event broadcaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// This is an empty shell method with no code in it
// STEP 9: Initialize some special beans
onRefresh();
// Check for listener beans and register them.
// Wait for some listener registrations
STEP 10: Register some listeners
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// key, key
// Complete the bean instantiation
// STEP 11: Instantiate the remaining singleton beans (not lazy-loaded)
// Note: The IoC, DI, and AOP of beans all occur in this step
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// STEP 12: Publish the corresponding events when the refresh is complete
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
Then check the org. Springframework. Context. Support. AbstractRefreshableApplicationContext# refreshBeanFactory method.
/** * 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. * Actually call the method * destroy the previous container * create a new 'IoC container' * load the 'BeanDefinition' object to register with the IoC container */
@Override
protected final void refreshBeanFactory(a) throws BeansException {
// If there was an IOC container before, destroy it
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
/ / create the IOC container, namely DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// Set factory properties: Whether BeanDefinition overrides are allowed and whether circular dependencies are allowed
customizeBeanFactory(beanFactory);
// Load the BeanDefinition object and register it in the IOC container (important)
/ / call real AbstractXmlApplicationContext loadBeanDefinitions method in class
// Focus on this step
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
The BeanFactory is created in the second step: obtainFreshBeanFactory method
The sequence diagram for BeanFactory creation is as follows:
Be defidefifinition loading, resolution and registration subprocesses
The entry is in the same: obtainFreshBeanFactory method as above
/** * 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. * Actually call the method * destroy the previous container * create a new 'IoC container' * load the 'BeanDefinition' object to register with the IoC container */
@Override
protected final void refreshBeanFactory(a) throws BeansException {
// If there was an IOC container before, destroy it
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
/ / create the IOC container, namely DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// Set factory properties: Whether BeanDefinition overrides are allowed and whether circular dependencies are allowed
customizeBeanFactory(beanFactory);
// Load the BeanDefinition object and register it in the IOC container (important)
/ / call real AbstractXmlApplicationContext loadBeanDefinitions method in class
// Focus on this step
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
LoadBeanDefinitions (beanFactory
Let’s look at the way based on XML AbstractXmlApplicationContext class
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see#loadBeanDefinitions * * Actually calls this method */
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// Create a BeanDefinition reader to actually load and register BeanDefinition by reading the XML file
// This is a delegate mode
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);
/ / the key
// Delegate to BeanDefinition reader to load BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}
Copy the code
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// Obtain the location of the resource
// getConfigResources is an empty implementation. The real implementation calls the subclass's method to get the resource location
/ / such as: during the ClassPathXmlApplicationContext implementation
/ / and FileSystemXmlApplicationContext without using this method
Resource[] configResources = getConfigResources();
if(configResources ! =null) {
/ / XML Bean reader calls its parent class AbstractBeanDefinitionReader loadBeanDefinitions reads in the positioning of the resources
/ / the key
reader.loadBeanDefinitions(configResources);
}
/ / if a subclass of resource location is empty, then obtain FileSystemXmlApplicationContext setConfigLocations method set up resources in the constructor
String[] configLocations = getConfigLocations();
if(configLocations ! =null) {
/ / XML Bean reader calls its parent class AbstractBeanDefinitionReader reads the positioning of the resources
/ / the keyreader.loadBeanDefinitions(configLocations); }}Copy the code
Then: org. Springframework. Beans. Factory. Support. AbstractBeanDefinitionReader# loadBeanDefinitions method
// Actually call the method
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
// If there are multiple config files, loop through the load and count how many beanDefinitions are loaded
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
Copy the code
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
/ / the key
return loadBeanDefinitions(location, null);
}
Copy the code
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// Gets the resource loader set during IOC container initialization
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 calls methods of its subclass XmlBeanDefinitionReader for loading
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 + "]");
}
returnloadCount; }}Copy the code
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
/ / key points:
// Actually call the loadBeanDefinitions method in subclass XmlBeanDefinitionReader
counter += loadBeanDefinitions(resource);
}
return counter;
}
Copy the code
Then: org. Springframework. Beans. Factory. XML. XmlBeanDefinitionReader# loadBeanDefinitions method
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// Perform special encoding on the read XML resources
// Proceed to call the loadBeanDefinitions method below in this class
return loadBeanDefinitions(new EncodedResource(resource));
}
Copy the code
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);
}
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 {
// Convert the resource file to an I/O stream of InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// Get the parsing source of the XML from InputStream
InputSource inputSource = new InputSource(inputStream);
if(encodedResource.getEncoding() ! =null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// Here is the specific reading process
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
**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see#registerBeanDefinitions * This method is actually called * in this class, XmlBeanDefinitionReader */protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// Load and parse the XML file through dom4j to form the Document object
Document doc = doLoadDocument(inputSource, resource);
// Load and register BeanDefinition by operating on the Document object
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); }}Copy the code
Org. Springframework. Beans. Factory. XML. XmlBeanDefinitionReader# doLoadBeanDefinitions method
The above is divided into two steps:
- Use dom4J to parse the ApplicationContext.xml configuration file into a Document object
- Will parse the Document object, and encapsulated into BeanDefinition object, and finally into the DefaultListableBeanFactory created above (beanDefinitionMap in understandable into the Ioc container).
The first step
Use dom4J to parse the ApplicationContext.xml configuration file into a Document object
/**
* Actually load the specified document using the configured DocumentLoader.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the DOM Document
* @throws Exception when thrown from the DocumentLoader
* @see #setDocumentLoader
* @seeDocumentLoader#loadDocument * loads and parses the XML file through dom4j to form the Document object */
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
Copy the code
The specific analysis behind is not specific to see.
The second step
Will parse the Document object, and encapsulated into BeanDefinition object, and finally into the DefaultListableBeanFactory created above (beanDefinitionMap in understandable into the Ioc container).
Call the XmlBeanDefinitionReader registerBeanDefinitions method ** /
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
/ / create BeanDefinitionDocumentReader used to parse the Document object
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// Get the number of registered beans in the container
int countBefore = getRegistry().getBeanDefinitionCount();
/ / the parsing process, entrance BeanDefinitionDocumentReader just an interface
Completed in DefaultBeanDefinitionDocumentReader / / concrete implementation process
/ / the key
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// Count the number of newly registered beans
return getRegistry().getBeanDefinitionCount() - countBefore;
}
Copy the code
Then: org. Springframework. Beans. Factory. XML. DefaultBeanDefinitionDocumentReader# registerBeanDefinitions method
/** * to parse the Document object */
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// Get the root node first
// Get the
tag for the root element of the Document
Element root = doc.getDocumentElement();
/ / the key
doRegisterBeanDefinitions(root);
}
Copy the code
/**
* Register each bean definition within the given root {@code< beans / >} element. * key * or call * 1 doRegisterBeanDefinitions method in the class. Using the delegate pattern, here will be specific to BeanDefinitionParserDelegate BeanDefinition analytical work to complete the * 2. Perform custom parsing before parsing Bean definitions to make the parsing process more extensible * 3. Entrusted to BeanDefinitionParserDelegate, starting from the root element of the Document to BeanDefinition parsing * * /
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,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
/ / use the delegate pattern, here will be specific to BeanDefinitionParserDelegate BeanDefinition analytical work to complete
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
/ / determine whether namespace http://www.springframework.org/schema/beans
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; }}}// Perform custom parsing before parsing Bean definitions to make the parsing process more extensible
preProcessXml(root);
/ / entrusted to BeanDefinitionParserDelegate, starting from the root element of the Document to BeanDefinition parsing
/ / the key
parseBeanDefinitions(root, this.delegate);
// After the Bean definition is parsed, custom parsing is performed to increase the extensibility of the parsing process
postProcessXml(root);
this.delegate = parent;
}
Copy the code
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @paramThe DOM root element of the document is also called by the parseBeanDefinitions method, which is an AOP entry
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// Does the loaded Documen object use Spring's default XML namespace (beans namespace)
/ / the beans in the tag XMLNS for http://www.springframework.org/schema/beans
if (delegate.isDefaultNamespace(root)) {
// Get all child nodes of the Document object root element (bean tags, Import tags, alias tags and other custom tags context, AOP, etc.)
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;
// For bean tags, import tags, and alias tags, parsing rules are used by default
// Note that any tag without a colon belongs to the default namespace, such as
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
// Elements like context tags, AOP tags,tx tags are parsed using user-defined parsing rules
else{ delegate.parseCustomElement(ele); }}}}else {
// If it is not the default namespace, the element nodes are resolved using user-defined parsing rulesdelegate.parseCustomElement(root); }}Copy the code
// Call the method in this class
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// Parse
tags
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// Parse the
tag
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// Parse the
tag highlights
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// Parse the built-in
tag
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
// recursive calldoRegisterBeanDefinitions(ele); }}Copy the code
/** * Process the given bean element, Parsing the bean definition * and registering it with the registry
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// Parse the
tag to get the BeanDefnition focus
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if(bdHolder ! =null) {
// Decorate the BeanDefinition object if necessary
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
/ / the key
/ / register ultimately BeanDefinition to BeanDefinitionRegistry (namely DefaultListableBeanFactory)
/ / because DefaultListableBeanFactory BeanDefinitionRegistry is realized
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
The above is divided into two steps:
- First parse the Document into a BeanDefinitionHolder and put it in a BeanDefinitionHolder.
- BeanDefinition object into beanDefinitionMap DefaultListableBeanFactory objects.
1. Parse the Document and encapsulate it into BeanDefinition and place it in BeanDefinitionHolder.
Then: org. Springframework. Beans. Factory. XML. BeanDefinitionParserDelegate# parseBeanDefinitionElement method
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@linkOrg. Springframework. Beans. Factory. Parsing. ProblemReporter}. * really calling the method: * /
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
Copy the code
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@linkOrg. Springframework. Beans. Factory. Parsing. ProblemReporter}. * really calling the method: * /
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// Get the value of the attribute ID in the
tag
String id = ele.getAttribute(ID_ATTRIBUTE);
// Get the value of the attribute name in the
tag
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if(! StringUtils.hasText(beanName) && ! aliases.isEmpty()) { beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases"); }}if (containingBean == null) {
// Check whether the id or name of the bean is unique
checkNameUniqueness(beanName, aliases, ele);
}
// Parse the
tag to get the BeanDefinition object: important
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if(beanDefinition ! =null) {
if(! StringUtils.hasText(beanName)) {try {
if(containingBean ! =null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards.
String beanClassName = beanDefinition.getBeanClassName();
if(beanClassName ! =null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); }}if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]"); }}catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// Encapsulate BeanDefinitionHolder in the BeanDefinitionHolder data structure
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@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
// Get the parent attribute of the bean label
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
GenericBeanDefinition; GenericBeanDefinition; GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// Set the attributes of the following parsed bean tags into the created BeanDefinition object, that is, encapsulate them all into the BeanDefinition object
// Parse the attributes of the
tag
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// Parse the child tags of the
tag
// Parse the
tag
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// Parse
tags
parseMetaElements(ele, bd);
// parse the
tag
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// Resolve the < event-method > tag
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
// the
tag is used ----
parsePropertyElements(ele, bd);
Parse the
tag
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
/**
* Create a bean definition for the given class name and parent name.
* @param className the name of the bean class
* @param parentName the name of the bean's parent bean
* @return the newly created bean definition
* @throwsClassNotFoundException if bean class resolution was runtime but failed
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
Copy the code
Then: org. Springframework. Beans. Factory. Support. BeanDefinitionReaderUtils# createBeanDefinition method
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
// Creating the BeanDefinition object ---- finally comes to an end
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
2. BeanDefinition object into beanDefinitionMap DefaultListableBeanFactory objects.
Then: org. Springframework. Beans. Factory. Support. BeanDefinitionReaderUtils# registerBeanDefinition method
/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throwsBeanDefinitionStoreException if registration failed * really calling this method is very important, * * / put bd bdMap
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
// Split BeanDefinitionHolder key and value
// Use this method to register bd into bdMap
/ / call the registerBeanDefinition DefaultListableBeanFactory complete real BeanDefinition registration
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
Finally: org. Springframework. Beans. Factory. Support. DefaultListableBeanFactory# registerBeanDefinition method
// This method registers BeanDefinition with bdMap
@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");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if(existingDefinition ! =null) {
if(! isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + existingDefinition + "] bound.");
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isWarnEnabled()) {
logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]"); }}else if(! beanDefinition.equals(existingDefinition)) {if (logger.isInfoEnabled()) {
logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]"); }}else {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]"); }}this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
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<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons; }}}else {
// Still in startup registration phase
/ / the key
/ / DefaultListableBeanFactory BeanDefinitionHolder of bd and beanName into the map in the collection
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if(existingDefinition ! =null|| containsSingleton(beanName)) { resetBeanDefinition(beanName); }}Copy the code
conclusion
Parse the contents of the ApplicationContext.xml configuration file into a Document object using Dom4j, then parse the Document object and wrap it into a BeanDefinition object, Finally BeanDefinition object into DefaultListableBeanFactory object Map < String, BeanDefinition > beanDefinitionMap, the key to the bean’s id, BeanName, value is the corresponding BeanDifinition object, that is, the definition information of the class, such as beanName, singleton, lazy loading, attribute information, initialization method, etc.