preface
Create Bean process are introduced in the finishBeanFactoryInitialization probably process, here into the singleton Bean creation process.
There are three main parts to creating a singleton Bean
- getSingleton
- createBean
- getObjectForBeanInstance
Enter the source code below:
getSingleton
public Object getSingleton(String beanName, ObjectFactory
singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
/ / lock
synchronized (this.singletonObjects) {
// Check if there are singletonObjects in the cache
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// Check if destruction is being performed
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!) ");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
/ / add Bean to the singletonsCurrentlyInCreation collection, said it was created
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// Call the factory method
Call createBean(beanName, MBD, args)
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throwex; }}catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); }}throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
/ / create success, from singletonsCurrentlyInCreation removed
afterSingletonCreation(beanName);
}
if (newSingleton) {
// Adds the given singleton object to the factory's singleton cache
// this.singletonObjects.put(beanName, singletonObject);
// this.singletonFactories.remove(beanName);
// this.earlySingletonObjects.remove(beanName);
// this.registeredSingletons.add(beanName);addSingleton(beanName, singletonObject); }}returnsingletonObject; }}Copy the code
Returns the (original) singleton registered with the given name or, if not already registered, creates and registers a new object.
This piece can be broken down into three parts:
1. Retrieve singletonObjects from the cache
What is a singletonObject?
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
Copy the code
SingletonObjects is a ConcurrentHashMap that is used to cache instances of singletonObjects.
2. Create singletonObject
If no singletonObject is retrieved from the cache, create a new object
singletonObject = singletonFactory.getObject();
This step essentially calls the createBean(beanName, MBD, args) method outside, which is a factory method. With the createBean method, a new singletonObject is created.
3. Add the created singletonObject to the cache
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
// The singleton has been successfully created
this.registeredSingletons.add(beanName); }}Copy the code
This step involves three caches and a list of successfully created singletons.
/** Cache of singleton objects: bean name to bean instance. */
/** Cache the singleton object, K -v -> beanname-bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
/** Cache Bean factory */
private finalMap<String, ObjectFactory<? >> singletonFactories =new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
/** Cache the early singleton */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
/** List of registered singletons, save BeanName in registered order. * /
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
Copy the code
Add the created singleton to the singleton cache and remove the corresponding object from the factory cache and the earlier singleton cache.
createBean
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
// Get the real typeClass<? > resolvedClass = resolveBeanClass(mbd, beanName);if(resolvedClass ! =null&&! mbd.hasBeanClass() && mbd.getBeanClassName() ! =null) {
// Create a new MBD to prevent other threads from modifying it
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
Validate and prepare the method substitution defined for this bean. Checks whether a method with the specified name exists.
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// Apply the post-handler before instantiation to resolve whether the specified bean has an instantiation shortcut.
/ / rear InstantiationAwareBeanPostProcessor processor
/ / Bean postProcessBeforeInstantiation method may has already been instantiated
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if(bean ! =null) {
returnbean; }}catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
// Instantiate the Bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); }}Copy the code
This method involves creating the Bean instance, populating the Bean, and applying the PostProcessor.
The instantiated Bean is in the doCreateBean. Now focus on the doCreateBean method.
doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
// Object wrapper for beans
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// Fetch from the cache
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
BeanInstance = BeanInstance = BeanInstance = BeanInstance
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Get the Bean instance and typeObject bean = instanceWrapper.getWrappedInstance(); Class<? > beanType = instanceWrapper.getWrappedClass();if(beanType ! = NullBean.class) { mbd.resolvedTargetType = beanType; }// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if(! mbd.postProcessed) {try {
// Change MBD if allowed
/ / call the rear MergedBeanDefinitionPostProcessor processor
// postProcessMergedBeanDefinition(mbd, beanType, beanName);
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true; }}// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// MBD is a singleton and allows circular references (default true) and is being created
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
/ / get Bean references before, first obtained from the beanPostProcessorCache SmartInstantiationAwareBeanPostProcessor
/ / then from SmartInstantiationAwareBeanPostProcessor# getEarlyBeanReference for reference before
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// Attribute assignment
populateBean(beanName, mbd, instanceWrapper);
// Execute init
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); }}// Circular dependencies are allowed here
if (earlySingletonExposure) {
// Get the early Bean, not if there are no loop dependencies
Object earlySingletonReference = getSingleton(beanName, false);
// There are loop dependencies
if(earlySingletonReference ! =null) {
// Create the same object, there may be proxy objects
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// Get the dependent Bean and loop it into the actualDependentBeans
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if(! actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); }}}}// Register bean as disposable.
try {
// Register the destruction method
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
Copy the code
The same code is very long very long!
Step by step:
If the Bean is a singleton Bean that allows circular references and is being created, then a circular reference is in place. The call:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
This line of code involves two methods, getEarlyBeanReference and addSingletonFactory
- getEarlyBeanReference
- addSingletonFactory
In this section you can see that the singletonFactory of a singleton object created is added to the singletonFactories cache.
Also remove the singleton from the earlySingletonObjects cache.
So when do you add it to the earlySingletonObjects cache?
This can refer to the Spring source study 15: finishBeanFactoryInitialization in getSingleton method put in.
For convenience, I posted this little piece of code as well:
Here you move the cache from singletonFactories to earlySingletonObjects.
Spring’s beans are instantiated using the following level of caching:
SingletonObjects: level 1 cache, store singletonObjects, Bean already instantiated, initialized.
EarlySingletonObjects: a secondary cache that stores singletonObjects. This Bean is instantiated and has not been initialized.
SingletonFactories: A three-level cache that stores singletonFactories
The Bean will be initialized next
Here focus on the following parts:
- populateBean
Assign a value to the Bean’s properties.
The important thing to note here is that when assigning a property and finding that it depends on another Bean, the other Bean will be created first.
The @autowired annotation I’m using here does the following:
When properties are parsed here, internal dependent beans are created.
- initializeBean
getObjectForBeanInstance
Gets the object of the given bean instance or, in the case of a FactoryBean, the object created by the bean instance itself.
This is a relatively simple piece of logic, which is to create the Bean instance based on the type of the beanInstance you created earlier.
conclusion
This article mainly introduces the creation of a singleton Bean, of course, are large chunks of source code, need patience to chew.
After reading the source code, you basically have a detailed understanding of circular dependencies. You know that Spring uses level 3 caching to handle circular dependencies when initializing beans, and there will be a separate article on circular dependencies later.
Related to recommend
- Spring source study 15: finishBeanFactoryInitialization (key)
- The Spring source study 14: initApplicationEventMulticaster
- Spring source learning 13: initMessageSource