1. Questions
-
1. What is a spring container?
-
2. How does the spring container start?
-
3. What is the nature of the spring container?
-
4. What role does the spring container play in the spring family?
-
5. spring container design ideas?
2. Keyword
Container, Session, Context, Factory, Registry, Resolution, Definition, Initialization, Lazy Loading BeanFactory, BeanDefinition, ApplicationContext
3. Full Text Summary
The spring container is essentially a unit of definition that stores properties and methods describing different objects. When needed, objects are created according to the reflection mechanism, and then the described properties are initialized.It involves a series of sophisticated design patterns and implementation ideas that provide an excellent template for writing standard, high-quality code.This article seeks to discuss the core mechanisms of spring containers and to clarify the nature of spring containers with minimal code.
4. Architecture
4.1 Spring Overall Architecture
The above figure is the overall architecture of the Spring framework, from which we can see that the foundation of Spring is core container, which is what we call IOC container.This is the basis for the prosperity of AOP, DATA and WEB. In this chapter we discuss the foundation of the spring family, the spring container, which is the IOC container we mentioned above.All other spring components are built on containers, so we've temporarily removed all other component feature descriptions and only discussed IOC containers.
The three core jar packages of the spring container are beans, context, and core.Beans are the cornerstone of spring. Beans at the end of everything, contexts maintain the context of the application. If beans are actors, contexts are the stage, and cores are props.
4.1 Context ApplicationConext
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver { String getId(); String getApplicationName(); String getDisplayName(); long getStartupDate(); ApplicationContext getParent(); AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; }
-
ApplicationConext: From the inheritance relationship of the class diagram, we see that the underlying class ApplicationConext inherits five capabilities: resource, message, event, environment, factory. ApplicationConext contains only simple read-only properties.
-
Configurable ApplicationContext: Inherits the life cycle management capabilities, while inheriting the ApplicationConext, expands the context's environment, events, and other write properties.
-
AbstractApplicationContext: Most of the capabilities are implemented in this class definition, which inherits the class load capability interface DefaultResourceLoader and Configurable ApplicationContext that reads and writes the context, and is parsed in detail by the process started by the ioc container.
-
GenericApplicationContext: Common context,
-
AnnotationConfigApplicationContext: Annotation configurable context,
-
GenericGroovyApplicationContext:groovy configuration file context,
-
GenericXml ApplicationContext: General xml profile context,
-
StaticApplicationContext: Message readable context,
-
AbstractRefreshableApplicationContext: Refreshable configurable context,
-
AbstractRefreshableConfigApplicationContext: Refreshable configurable context,
-
The context of the AbstractXmlApplicationContext:xml configuration file type,
-
ClassPathXmlAp-plicationContext: The terminal class path xml context,
-
FileSystemXmlApplicationContext: File system xml context,
4.2BeanFactory
spring's world is all about bean s.
-
AliasRegistry: Alias Registry
-
BeanFactory: Factory
-
BeanDefinitionRegistry:
DefaultListableBeanFactory, which converges so much of the upper level's capabilities, specifically includes the core BeanDefinitionRegistry and BeanFactory, which is the factory where bean s are defined and produced.
5. Process
The container launches the core process as shown below, which consists of creating an object after the container reads the configuration file, then initializing the object properties:
5.1 Startup Container
To explore spring's core container principles, you need to eliminate the interference of other advanced properties, build your project with the least jar packages, and then follow the container's starting process step by step.
-
Build Project: Create a new java project, introducing spring packages with minimal dependencies
New Test Startup Class Application
package com.alibaba.spring;import com.alibaba.spring.beans.Producer;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * @author miumiu */public class Application { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Producer producer = context.getBean(Producer.class); System.out.println("running the test case with name = " + producer.getCount()); } }
-
New spring Profile
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean name="producer" class="com.alibaba.spring.beans.Producer"> <property name="count" value="10" /> </bean></beans>
-
results of enforcement
July 10, 2018 11:31:16 morning org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh //Information: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5197848c: startup date [Tue Jul 10 11:31:16 CST 2018]; root of context hierarchy //July 10, 2018 11:31:17 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions //Information: Loading XML bean definitions from class path resource [applicationContext.xml] running the test case with name = 10
After these steps, we have successfully created the objects described in the configuration file using spring's IOC container, and we no longer need to create objects using the new form. Here's a workflow for dissecting the IOC container in depth.
5.2 Entry
-
Create the spring container context object, call the ClassPathXmlApplicationContext constructor, and pass in the configuration file path parameter
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");/** * loading the definitions * from the given XML file and automatically refreshing the context */public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); }public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); //Core approach } }
-
Call the refresh() method to start the IOC container
/** * Load or refresh the persistent representation of the configuration */ public void refresh() throws BeansException, IllegalStateException { //Container restarts synchronous monitoring lock to prevent refresh from being repeated halfway through synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //Fill in profile placeholders, record container startup time and startup status prepareRefresh(); //Complete the process of defining the configuration file to the registry registration bean s, at which point the object has not been created // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. //Configure class loader, customize special bean s, add BeanPostProcessor for callback prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //The factory loads the configuration and calls back the PostProcessBeanFactory as a factory extension before initialization postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //Call the implementation of the extension interface PostProcessBeanFactory registered above, which is a list type for the extension interface invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // bean extension: postProcessBeforeInitialization and postProcessAfterInitialization //Executed before and after Bean initialization, respectively registerBeanPostProcessors(beanFactory); // Initialize message source for this context. //Initialize MessageSource object, internationalize initMessageSource(); // Initialize event multicaster for this context. //Initialize event broadcaster initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //Temporary hook method, which provides some special operations before initialization is complete, portal onRefresh(); // Check for listener beans and register them. //Register listener registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //Instantiate the bean s in the factory registry (unless lazily loaded, used to instantiate) finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //Initialize life cycle, broadcast events 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(); } } }
5.3 Preparations
-
Initialization before container initialization
protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Initialize any placeholder property sources in the context environment //Initialization Placeholder initPropertySources(); // Validate that all properties marked as required are resolvable // see ConfigurablePropertyResolver#setRequiredProperties //Verify Profile getEnvironment().validateRequiredProperties(); // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); }
5.4 Create BeanFactory
-
Create BeanFactory, which is one of the two core modules of the entire IOC container startup process
//AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //Close old factories and create new ones refreshBeanFactory(); //Return to the new factory created ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
-
Reset BeanFactory, destroy if present, create if not present
//AbstractRefreshableApplicationContext.java 120
@Override protected final void refreshBeanFactory() throws BeansException { //If BeanFactory() exists, it will be destroyed, that is, the objects stored in the Map in the factory will be cleared if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //Return to the newly created DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); //Factory identification id beanFactory.setSerializationId(getId()); //Set whether the container allows object overrides, circular dependencies 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); } }
-
Create BeanFactory, construct subclass object DefaultListableBeanFactory, we can see the importance of this class from the previous architecture section, which covers factory BeanFactory, object definition BeanDefinition, and registry AliasRegistry capabilities, is a complete object factory, the following is to use this factory to convert configuration file information into class definition information.Then the object is created and the attribute is assigned.
//AbstractRefreshableApplicationContext.java 199
protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); }
5.5 Read Configuration File
-
Read xml configuration file
//AbstractXmlApplicationContext.java 80
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //XML file reader instantiating a factory class 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. //Open xml file check to close subclasses of the implementation initBeanDefinitionReader(beanDefinitionReader); //beanDefinitionReader Reader Load Resource File loadBeanDefinitions(beanDefinitionReader); }
-
Load resource file
//AbstractXmlApplicationContext.java 120
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //CongLocations eventually resolves to the resource object configResources with the same effect as the branch above String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
-
Load all resource files in a loop
//AbstractBeanDefinitionReader.java 177
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { counter += loadBeanDefinitions(resource); } return counter; }
-
Reader gets path from resource object and reads configuration file
//XmlBeanDefinitionReader.java 303
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
//XmlBeanDefinitionReader.java 314
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()); } //Use ThreadLocal to store profile paths 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!"); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //Open the file stream to read the contents of the file 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(); } } }
-
Read Profile Content
//XmlBeanDefinitionReader.java 388
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //Number of Document Files Generated by xml Files 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); } }
5.6 Registration Objects
-
Register object, convert beans described by XML configuration file to BeanFactory's registry, return incremental number of beans
//XmlBeanDefinitionReader.java 505
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //Number of Definition s already exist in the registry (description of object) int countBefore = getRegistry().getBeanDefinitionCount(); //Register the bean definition of the doc tree with the registry properties of the factory class documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
-
document reader, loads objects that have been converted into memory documents into the registry
//DefaultBeanDefinitionDocumentReader.java 90
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }
-
Traverse document s to parse xml tags one by one
//DefaultBeanDefinitionDocumentReader.java 116
protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In BeanDefinitionParserDelegate parent = this.delegate; //Create a tool to parse DOM tree objects BeanDefinitionParserDelegate this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { //If you do not include the <profile>tag, skip the operation, unnecessary method, skip 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);//Reserve method, expand operation before parsing parseBeanDefinitions(root, this.delegate);//Core method, parsing DOM tree postProcessXml(root);//Reserve method, expanded operation after parsing this.delegate = parent; }
5.7 Parse Profile
-
Parse Memory DOM File Tree
//DefaultBeanDefinitionDocumentReader.java 161
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; //Default namespace containing http://www.springframework.org/schema/beans if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { //Non-default namespaces are <mvc/>, <context/>, <aop/>, etc. delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
-
Resolve labels for default namespaces
//DefaultBeanDefinitionDocumentReader.java 182
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate);//Resolve labels with namespace bean s } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
-
Parse label details, create a new BeanDefinition wrapper class, hold BeanDefinition references, beanName, and alias
//DefaultBeanDefinitionDocumentReader.java 298
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //Convert DOM tree object to BeanDefinition wrapper class bdHolder BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
//BeanDefinitionParserDelegate.java 427
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); }
//BeanDefinitionParserDelegate.java 436
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>(); 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) { checkNameUniqueness(beanName, aliases, ele); } //DOM tree label mapped to BeanDefinition object 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 compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
//BeanDefinitionParserDelegate.java 521
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } //Create BeanDefinition object and set corresponding class name AbstractBeanDefinition bd = createBeanDefinition(className, parent); //Parse tags inside xml into BeanDefinition objects one by one parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } 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; }
-
So far we have successfully mapped the label properties from a single xml profile into the BeanDefinitionHolder object, and then we have registered the BeanDefinition object into the factory registry
//BeanDefinitionReaderUtils.java 143
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); //Register the BeanDefinition object of the definitionHolder with the beanName to the registry registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. //Register if there is an alias String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
-
Register BeanDefinition Object
//DefaultListableBeanFactory.java 793
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 oldBeanDefinition; //We've been fighting so long to convert xml to Definition and put it into beanDefinitionMap oldBeanDefinition = this.beanDefinitionMap.get(beanName); //New xml should get empty if (oldBeanDefinition != null) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else 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); } else { //The initialization process should not allow instantiation 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 { //The core step is to add a beanDefinition to the beanDefinition Map // 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)) { resetBeanDefinition(beanName); } }