Spring Loads Beans, Instantiates Beans, Gets Bean Processes
The purpose of this paper is to analyze the process of Spring loading beans, instantiating beans, and getting beans by analyzing the source code. Some of the core content will be explained in the source code.Content is dull, careful!
- Step one, start a Spring project
Spring Startup Entry
from Official Documents Here's how you can get started with Spring.
We passed in oneTest.xmlfile
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring/test.xml");
- xml file content
<?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.xsd "> <bean id="stringutil" class="com.baigt.utils.StringUtils"></bean> </beans>
- bean object (StringUtils)
public class StringUtils{ // some code }
Bean parsing for source analysis
Analyzing Bean parsing process, instantiation process from source level,
Entry ClassPathXmlApplicationContext
Look at the class diagram first, and the code in different levels will be used repeatedly in subsequent source code.
ClassPathXmlApplicationContext instantiation
public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } // Omit some code public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); //The current ClassPathXmlApplicationContext is also a subclass of resource setConfigLocations(configLocations); //The refresh passed in front is true, executing the refresh method if (refresh) { refresh(); } }
AbstractApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#refresh,AbstractApplicationContext is the parent of ClassPathXmlApplicationContext.Here refresh overrides the refresh method of Configurable ApplicationContext
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Pre-preparation for refresh, such as setting startTime, turning on activation state, closing state, etc. prepareRefresh(); //Tell the subclass to refresh the content Bean factory (it is obvious from the class diagram that the entry class instance is also a bean factory) // Watch and refresh BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //Pre-set some properties used in this context, such as Classloader, Callback, environment, etc. prepareBeanFactory(beanFactory); try { // Notify the context subclass and post-process the beanFactory.For example, use a series of Webcontext subclasses postProcessBeanFactory(beanFactory); // Register BeanFactoryPostProcessor as a Bean in the current Context invokeBeanFactoryPostProcessors(beanFactory); // Register BeanPostProcessor to intercept post-processing of Bean creation. registerBeanPostProcessors(beanFactory); // Initialize MessageSource for the current Context initMessageSource(); // Initialize application event broadcasting for the current context initApplicationEventMulticaster(); // Initialize other special bean objects such as webcontext onRefresh(); // Check the listening beans and publish them (ApplicationListener) registerListeners(); // Instantiate all other collection of singleton objects in BeanFactory (non-delayed) finishBeanFactoryInitialization(beanFactory); // Finally, publish Lifecycle Processor and ContextRefreshedEvent events finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Failed to destroy bean s 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(); } } }
BeanFactory creation
If you know the BeanFactory, you know this class is an important foundation object for Spring.
Extend BeanFactory explanation
-
Is the root interface to access the Spring Bean container.Is a basic client, more features such as ListableBeanFactory, ConfigurableBeanFactory.
-
The implementation classes of this interface generally hold multiple BeanDefinition object information, such as number, Scope, instance, name, and so on.(comparable ListableBeanFactory)
-
BeanFactory is a central registry (registration) for the Application component
-
Typically, BeanFactory loads Bean data from configuration sources such as Xml and stores it in the factory.
-
In the case of HierarchicalBeanFactory, almost all methods in ListableBeanFactory check to see if they are parent factories.
-
beanFactory should support the following standard life cycles
<li>BeanNameAware's {@code setBeanName} * <li>BeanClassLoaderAware's {@code setBeanClassLoader} * <li>BeanFactoryAware's {@code setBeanFactory} * <li>EnvironmentAware's {@code setEnvironment} * <li>EmbeddedValueResolverAware's {@code setEmbeddedValueResolver} * <li>ResourceLoaderAware's {@code setResourceLoader} * (only applicable when running in an application context) * <li>ApplicationEventPublisherAware's {@code setApplicationEventPublisher} * (only applicable when running in an application context) * <li>MessageSourceAware's {@code setMessageSource} * (only applicable when running in an application context) * <li>ApplicationContextAware's {@code setApplicationContext} * (only applicable when running in an application context) * <li>ServletContextAware's {@code setServletContext} * (only applicable when running in a web application context) * <li>{@code postProcessBeforeInitialization} methods of BeanPostProcessors * <li>InitializingBean's {@code afterPropertiesSet} * <li>a custom init-method definition * <li>{@code postProcessAfterInitialization} methods of BeanPostProcessors * </ol> * * <p>On shutdown of a bean factory, the following lifecycle methods apply: * <ol> * <li>{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors * <li>DisposableBean's {@code destroy} * <li>a custom destroy-method definition
AbstractApplicationContext#obtainFreshBeanFactory
- Continue BeanFactory creation process analysis
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // If a factory already exists, destroy it directly before rebuilding it refreshBeanFactory(); //Basically from the beanFactory created above ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
AbstractRefreshableApplicationContext#refreshBeanFactory
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory.If a factory already exists, destroy it directly and rebuild it.The new factory loadBeanDefinitions.
Extended BeanDefinition Terms
BeanDefinition describes the properties that a Bean instance has, such as Scope (singleton or Prototype),beanClassName, whether it is delayed loading, dependencies, and so on (and other properties).Of course, this is the smallest BeanDefiniton interface, and RootBeanDefinition is a more powerful subclass implementation.
- Back to BeanFactory creation.
protected final void refreshBeanFactory() throws BeansException { //If a bean factory destroys beans, close the bean factory if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //Create DefaultListableBeanFactory, which is a subclass of BeanDefinitionRegistry DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //Load BeanDefinition (in this case, parse the Bean tag in Xml and load it into BeanDefinition) loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { //Assign to beanFactory in Context this.beanFactory = beanFactory; } } catch (IOException ex) { } }
AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory)
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory).Here's just an example of the current Xml approach that will enter here.
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create an XmlBeanDefinitionReader instance with the given beanFactory(BeanDefinitionRegistry) XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); // ClassPathXmlApplicationContext is also a ResouceLoader 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); // Generate BeanDefinition by XmlBeanDefinitionReader parsing loadBeanDefinitions(beanDefinitionReader); } //Connect protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } // Incoming in EntryTest.xmlStored inThis.configLocationsYes, you can get it here. String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...).Read from incoming path.
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; } //Connect public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } // Connect // The class diagram shows that this object (ClassPathXmlApplicationContext) is also an instance of DefaultResourceLoader and has been set into reader, so follow the else logic public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { 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); 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 resouce locations from absolute paths that were passed in for usTest.xmlRoute Resource resource = resourceLoader.getResource(location); //Let's follow up!!! int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
XmlBeanDefinitionReader#loadBeanDefinitions(Resource)
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource).Loading Beans from xml
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { //...omit code try { // Front we passed inTest.xmlInput Flow InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //True load bean definition from xml return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { } } // Connect protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // Parse xml file as Document Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { // Omit Code } } // Connect public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); // Register BeanDefinition from reader to factory documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions.Register BeanDefinition objects.
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); } // Followed by parsing the xml through delegation mode and template mode protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); // Omit code... preProcessXml(root); //Focus only on this code parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; } //Continue to focus only on Spring's own parsing 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; if (delegate.isDefaultNamespace(ele)) { // Spring's own tag resolution parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } //Connect to nested support for parsing Import tags, alias, bean s 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)) { //Attention should be paid to resolving the bean tag here in the example processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } } //Connect to process BeanDefinition protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // Core parsing steps (using delegation mode) BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register final decoration 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#parseBeanDefinitionElement(Element)
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element).True xml to BeanDefinition logic code.
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); } // Parse the xml, generate an AbstractBeanDefinition object instance, and give it to AbstractBeanDefinition management 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 the name tag is used, alias name 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); } // Parse element content into BeanDefinition 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); //Wrap as BeanDefinitionHolder object return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; } 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 basic BeanDefinition objects, set beanName, ParentName, ClassLoader, and so on AbstractBeanDefinition bd = createBeanDefinition(className, parent); //Resolve attribute tags such as singleton, lazy-init, scope, abstract, and so on, and set them to the BeanDefinition instance (hereinafter referred to as bd) parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); // Resolve description s tags set to Bd objects bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // Resolve meta tags set to bd objects parseMetaElements(ele, bd); // Resolve lookup-method tags set to bd objects parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // Resolve replaced-method tags set to bd objects parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // Resolve constructor-arg tags set to bd objects parseConstructorArgElements(ele, bd); // Resolve property tags set to bd objects (such as url s, user s, pwd properties that we inject when using datasource) parsePropertyElements(ele, bd); // Resolve qualifier tags set to bd objects 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; } protected AbstractBeanDefinition createBeanDefinition(String className, String parentName) throws ClassNotFoundException { return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); } org.springframework.beans.factory.support.BeanDefinitionReaderUtils#createBeanDefinition public static AbstractBeanDefinition createBeanDefinition( String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setParentName(parentName); if (className != null) { if (classLoader != null) { bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd; }
Return to DefaultBeanDefinition DocumentReader#processBeanDefinition
Layer 1 call returns, returns toOrg.springframework.beans.Factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition.We continue to analyze BeanDDefinitionReaderUtils.registerBeanDefinitionPart.This part is the step to actually associate BeanDefiniton with the current Context.
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 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)); } }
org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition
Give the BeanDefiniton Holder holds to the BeanDefinitionRegistry (the current context is also a registry instance, DefaultListableBeanFactory)
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); // Core focus on this logic 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); } } }
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
Enter this method for the first time, 1. beanDefinition is an instance of AbstractBeanDefinition type, validate;2. oldBeanDefinition is empty, execute else code 3. BeanCreationStarted is false, perform beanDefinitionMap, beanDefinitionNames, manualSingletonNames value operations.
- Pay special attention to these objects (beanDefinitionMap, beanDefinitionNames, manualSingletonNames), which you will use when instantiating and retrieving beans
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"); // validate if it is an instance of AbstractBeanDefinition type 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; oldBeanDefinition = this.beanDefinitionMap.get(beanName); // First entry old is 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 { //First false 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 { //It is still in the start-up registration phase (i.e., there is no real instantiation, just the property definition of the bean is taken, and it is not directly usable yet).) // 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); } }
SpringBean Creation
After BeanDefiniton is injected into the registry, step by step back to the AbstractApplicationContext#refresh method, where we focus only on the basic Bean instantiation process.
synchronized (this.startupShutdownMonitor) { try { // Mainly this code finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }
AbstractApplicationContext#finishBeanFactoryInitialization
- org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
finishBeanFactoryInitialization.Instantiate other normal collection of non-delayed loaded instances held by BeanFactory.Look down here at the "preInstantiateSingletons" method
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { //Omit code... // Instantiate other normal collection of non-delayed loaded instances held by BeanFactory beanFactory.preInstantiateSingletons(); }
DefaultListableBeanFactory#preInstantiateSingletons
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons.There are two types of beans created, FactoryBean and Ordinary Bean.Here we will only analyze ordinary beans.
- Notice the "this.beanDefinitionNames"Is"obtainFreshBeanFactory()"to create assignments.BeanDefinition of our StringUtils.
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { return ((SmartFactoryBean<?>) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } else { //We are focusing on this locality getBean(beanName); } } }
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
Continue to follow up
public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }
(singleton)org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
Here we are going to create an instance of the singleton (fetch it from the cache if you have one).The way to create it is through the createBean method in the ObjectFactory internal class implementation.Let's start with the logic of getSingleton.
// Logic that first comes in empty and last goes down Object sharedInstance = getSingleton(beanName); // adoptThis.beanDefinitionMapYou can see the source code yourself.It's not a very long story here. final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); //... if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { //Logic to ultimately create beans return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } //.... // Note here that the single cache is set up and can be used directly later if (newSingleton) { addSingleton(beanName, singletonObject); }
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
The core logic we're going to analyze here (instance singleton) is called ObjectFactory.getObject That is, we have the inner class above, and the implementation of the inner class is done through createBean.Let's look at the logic of createBean.
try { //Returning to the getObject method of the ObjectFactory of AbstractBeanFactory#doGetBean, execute the createBean method in it singletonObject = singletonFactory.getObject(); newSingleton = true; }
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
Subclass implementation of AbstractBeanFactory
AbstractAutowireCapableBeanFactory#createBean(java.lang.Class<T>) public <T> T createBean(Class<T> beanClass) throws BeansException { // Use prototype bean definition, to avoid registering bean as dependent bean. RootBeanDefinition bd = new RootBeanDefinition(beanClass); bd.setScope(SCOPE_PROTOTYPE); bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader()); return (T) createBean(beanClass.getName(), bd, null); } // Connect protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { RootBeanDefinition mbdToUse = mbd; Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { } try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { } // Instance Creation for Ordinary Bean s Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; }
AbstractAutowireCapableBeanFactory#doCreateBean
The core concern is the processing of instanceWrapper and exposedObject objects
BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //Create a Wrapper object for a bean instance instanceWrapper = createBeanInstance(beanName, mbd, args); } // bean instances have been created in wrapper (incomplete) final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); //Omit some code. // Initialize the bean instance. Object exposedObject = bean; try { // Inject property into Bean populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } //Omit some code. return exposedObject;
AbstractAutowireCapableBeanFactory#createBeanInstance
instanceWrapper = createBeanInstance(beanName, mbd, args); this method creates Bean objects for lass with a specified name and supports three strategies: parameterless, factoryMethod, AutoWireConstructor.Default execution of "instantiateBean" without parameters
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { // Make sure bean class is actually resolved at this point. Class<?> beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } // Normally we are not FactoryMethod and have a construct (or can have a hash) if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } // Need to determine the constructor... Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } //Use parameterless construction instances without special processing // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd); }
AbstractAutowireCapableBeanFactory#instantiateBean
Generate an instance through "getInstantiationStrategy().instantiate(mbd, beanName, parent)".
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { return getInstantiationStrategy().instantiate(mbd, beanName, parent); } }, getAccessControlContext()); } else { // This code logic is typically used to generate bean instances beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }
SimpleInstantiationStrategy#instantiate
org.springframework.beans.factory.support.SimpleInstantiationStrategy#Instantiate(Org.springframework.beans.Factory.support.RootBeanDefinition,Java.lang.String,Org.springframework.beans.Factory.BeanFactory).Get an instance of the lass construction method for the corresponding BeanName in BeanDefinition from bd and hand it to BeanUtils to instantiate the real bean object
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. if (bd.getMethodOverrides().isEmpty()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() { @Override public Constructor<?> run() throws Exception { return clazz.getDeclaredConstructor((Class[]) null); } }); } else { //Gets the Constructor instance of the Class defined by the bean tag in xml constructorToUse = clazz.getDeclaredConstructor((Class[]) null); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } //Instantiate a Class defined by a bean tag in xml return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(bd, beanName, owner); } }
Return to the AbstractAutowireCapableBeanFactory#doCreateBean method
Let's continue to look at what exposedObject does.Load Bean's Property, initBean object.Let's focus on the initializeBean method.
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Object exposedObject = bean; try { //Loading properties such as Property can be other Bean objects or basic properties such as String.See the applyPropertyValues method in populateBean populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { //Initialize Bean Object exposedObject = initializeBean(beanName, exposedObject, mbd); } }
AbstractAutowireCapableBeanFactory#initializeBean
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
Initialize the given Bean (execute BeanPost when RootBeanDefinition is empty), execute the related methods of wired (BeanNameAware, BeanClassLoaderAware, BeanFactoryAware), and execute initMethod.For example, InitializingBean#afterPropertiesSet(); or a custom init method.
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { //Method to execute wire s (BeanNameAware, BeanClassLoaderAware, BeanFactoryAware) invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //Execute init method invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
AbstractAutowireCapableBeanFactory#invokeInitMethods
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods.Execute the afterPropertiesSet method.If it is a custom init method, invokeCustomInitMethod is called.
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { ((InitializingBean) bean).afterPropertiesSet(); return null; } }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null) { String initMethodName = mbd.getInitMethodName(); if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
Return AbstractApplicationContext#refresh
This Bean instantiation is complete.
// Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);
SpringBean Get
Or our example, in the ApplicationContext instance we returned, there is a getBean method, so let's focus on the implementation of getBean.
//ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring/test.xml"); context.getBean(StringUtils.class);
AbstractApplicationContext#getBean
public <T> T getBean(Class<T> requiredType) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(requiredType); }
DefaultListableBeanFactory#getBean(java.lang.Class<T>)
org.springframework.beans.factory.support.DefaultListableBeanFactory#GetBean(Java.lang.Class<T>).The source code logic of resolveNamedBean is analyzed here.
public <T> T getBean(Class<T> requiredType) throws BeansException { return getBean(requiredType, (Object[]) null); } @Override public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException { //Get NamedBeanHolder Object Core Get SpringBean Logic NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args); if (namedBean != null) { //Finally, return to the instance here return namedBean.getBeanInstance(); } BeanFactory parent = getParentBeanFactory(); if (parent != null) { return parent.getBean(requiredType, args); } throw new NoSuchBeanDefinitionException(requiredType); } //Connect
DefaultListableBeanFactory#resolveNamedBean(java.lang.Class<T>, java.lang.Object...)
The source code in the example executes "new NamedBeanHolder<T> (beanName, getBean (beanName, requiredType, args)".BeanName=Test.xmlID defined in, that is, "stringutil".Below we will focus on the logic of the getBeanNamesForType and getBean methods.
//requiredType= StringUtil args=null private <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType, Object... args) throws BeansException { Assert.notNull(requiredType, "Required type must not be null"); // There is only one ["stringutil"] in the example of getting names based on Class type String[] candidateNames = getBeanNamesForType(requiredType); if (candidateNames.length > 1) { List<String> autowireCandidates = new ArrayList<String>(candidateNames.length); for (String beanName : candidateNames) { if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) { autowireCandidates.add(beanName); } } if (!autowireCandidates.isEmpty()) { candidateNames = StringUtils.toStringArray(autowireCandidates); } } // In our example, length==1, beanName=stringutil is consistent with the id we defined if (candidateNames.length == 1) { String beanName = candidateNames[0]; //Note the execution logic of the getBean method return new NamedBeanHolder<T>(beanName, getBean(beanName, requiredType, args)); } else if (candidateNames.length > 1) { Map<String, Object> candidates = new LinkedHashMap<String, Object>(candidateNames.length); for (String beanName : candidateNames) { if (containsSingleton(beanName)) { candidates.put(beanName, getBean(beanName, requiredType, args)); } else { candidates.put(beanName, getType(beanName)); } } String candidateName = determinePrimaryCandidate(candidates, requiredType); if (candidateName == null) { candidateName = determineHighestPriorityCandidate(candidates, requiredType); } if (candidateName != null) { Object beanInstance = candidates.get(candidateName); if (beanInstance instanceof Class) { beanInstance = getBean(candidateName, requiredType, args); } return new NamedBeanHolder<T>(candidateName, (T) beanInstance); } throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); } return null; }
DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class<?>)
Finally, when parsing the xml (loadBeanDefinition), use theThis.beanDefinitionNamesGet the beanName in the collection.See the source code for the "doGetBeanNamesForType" method below.
public String[] getBeanNamesForType(Class<?> type) { return getBeanNamesForType(type, true, true); } // type==StringUtil is not empty public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) { if (!isConfigurationFrozen() || type == null || !allowEagerInit) { return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit); } //cache is empty size==0 Map<Class<?>, String[]> cache = (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType); String[] resolvedBeanNames = cache.get(type); if (resolvedBeanNames != null) { return resolvedBeanNames; } // Will do this!!!! resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true); if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) { cache.put(type, resolvedBeanNames); } return resolvedBeanNames; } // fromThis.beanDefinitionNamesGet beanName in private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { List<String> result = new ArrayList<String>(); // Check all bean definitions. for (String beanName : this.beanDefinitionNames) { // Only consider bean as eligible if the bean name // is not defined as alias for some other bean. if (!isAlias(beanName)) { try { RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Only check bean definition if it is complete. if (!mbd.isAbstract() && (allowEagerInit || (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) && !requiresEagerInitForType(mbd.getFactoryBeanName()))) { // In case of FactoryBean, match object created by FactoryBean. boolean isFactoryBean = isFactoryBean(beanName, mbd); BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); boolean matchFound = (allowEagerInit || !isFactoryBean || (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) && (includeNonSingletons || (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) && isTypeMatch(beanName, type); if (!matchFound && isFactoryBean) { // In case of FactoryBean, try to match FactoryBean instance itself next. beanName = FACTORY_BEAN_PREFIX + beanName; matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type); } if (matchFound) { //This will be done last result.add(beanName); } } } catch (CannotLoadBeanClassException ex) { } catch (BeanDefinitionStoreException ex) { } } } return StringUtils.toStringArray(result); }
AbstractBeanFactory#getBean(String, Class<T>, Object...)
The process of creating a singleBean takes a closer look at the code created, which is not covered here.
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException { return doGetBean(name, requiredType, args, false); } protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; // Get from the cache. Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } //Omit code created by bean s return (T) bean; }
Return to the DefaultListableBeanFactory#getBean's "namedBean.getBeanInstance()"method
if (namedBean != null) { return namedBean.getBeanInstance(); }
Get Completed
Return to "Context.getBean(StringUtils.class";", get a Bean instance"
StringUtils sutils=context.getBean(StringUtils.class);
epilogue
The Spring version used for analysis is 4.3.18.If there are any discrepancies in the code, please distinguish them by yourself.Finally, if there are any errors, please correct them.Author: ricky QQ Group: 244930845.