Spring loop dependencies
In theory, Spring’s cyclic dependency should be discussed first after the instantiation and initialization of Spring beans. However, since THE author is also confused about the problem of cyclic dependency, in the spirit of breaking the casserole to ask the bottom, before writing the instantiation and initialization of Spring beans, we should first analyze Spring’s cyclic dependency. There are links in this article that are intended to help me and my fellow newcomers unravel the riddle of Spring cycle dependencies.
Four ways to inject Spring IOC
A setter method
A constructor
XML configuration file
Interface (obsolete)
Spring Level 3 Cache
/** Cache of singleton objects: bean name to bean instance. */
// Level 1 cache
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// Level 3 cache
/** Cache of singleton factories: bean name to ObjectFactory. */
private finalMap<String, ObjectFactory<? >> singletonFactories =new HashMap<>(16);
// Level 2 cache
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** The bean will be placed in the cache during creation, and the cache will be removed after successful creation. private final Set
singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
Copy the code
Question to consider: Why does Spring set up level 3 cache, if only to solve the problem of loop dependency, is level 1 cache sufficient, and level 2 cache sufficient? Talk about what Spring means by setting up a level 3 cache
sigeletonObjects
andearlySingletonObjects
As well assingletonFacotries
inDefaultSingletonBeanRegistry
As you can see,AbstractBeanFacotry
inheritedDefaultSingletonBeanRegistry
.
DefaultSingltonBeanRegistry
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
// allowEarlyReference stands for allowing circular references, which Spring can disable
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// singeletonObject == null indicates that singleObject is not fully initialized (instantiated, populated, initialized)
Object singletonObject = this.singletonObjects.get(beanName);
// Check to see if it is being created
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// Fetch the bean from the secondary cache (the bean is only instantiated in the secondary cache)
singletonObject = this.earlySingletonObjects.get(beanName);
//allowEarlyReference == true to allow cyclic dependencies
if (singletonObject == null && allowEarlyReference) {
// Lock level 1 cache, singleton
// If there is A ThreadA, ThreadbB, ThreadA with A and B cyclic dependencies, ThreadB with A and C cyclic dependencies.
When ThreadA and ThreadB are synchornized at the same time, lock contention occurs. Suppose that ThreadA acquires the lock first, and after it resolves the circular dependency, there is already A whole cache of A in the primary cache. After A releases the lock, C compets with other threads for the lock. It naturally gets A from level 1 cache
// Singleton double check
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// If there is still no bean, get it from level 3 cacheObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
if(singletonFactory ! =null) {
// Call getObject to get the bean,
singletonObject = singletonFactory.getObject();
// There are only instantiated beans in the early exposure object, so getObject just returns the instantiated bean.
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
// There is also a getSingleton method that can be analyzed later
Copy the code
addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// Add factory beans to the level 3 cache
this.singletonFactories.put(beanName, singletonFactory);
Corresponding level 2 cache / / delete the instantiation of bean, the author thinks that this is done to the thread safety and improve the efficiency of concurrent enclosing earlySingletonObjects. Remove (beanName);
// Register singleton
this.registeredSingletons.add(beanName); }}}Copy the code
AbstractBeanFactory
getBean
// Method of exposure
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException {
return doGetBean(name, requiredType, args, false);
}
Copy the code
doGetBean
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {
// This method is to get the full path name of the bean
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// This is where we call the getSingleton method in DefaultSingletonRegistry and implement double Check internally to ensure concurrency security.
Object sharedInstance = getSingleton(beanName);
if(sharedInstance ! =null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); }}// This method will be examined at the end of the article. Obviously, in the source code, this method is called on return, but by the time it is called, the loop-dependency resolution is over
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Get the parent BeanFacotry class to load
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// In prototype mode, Spring has no way to solve the problem of loop dependencies and naturally throws exceptions
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if(args ! =null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if(requiredType ! =null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return(T) parentBeanFactory.getBean(nameToLookup); }}// This method is important. If it is a type check, the bean has already been created
if(! typeCheckOnly) { markBeanAsCreated(beanName); }// Here we are, ready to start creating beans
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if(requiredType ! =null) {
beanCreation.tag("beanType", requiredType::toString);
}
/ / get MergedBeanDefition
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Check whether it is valid
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
//depensOn should have a @dependson annotation for this to have a value
String[] dependsOn = mbd.getDependsOn();
if(dependsOn ! =null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex); }}}// Create bean instance.
AbstractBeanDifition isSingleton() is a method in AbstractBeanDifition that checks whether this MND's Scope is a singleton
if (mbd.isSingleton()) {
// There is also a getSingleton, but this is not the one called above.
sharedInstance = getSingleton(beanName, () -> {
try {
// To the most important createBean method, as the name implies, this is where the logic to create the Bean is executed
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }else if (mbd.isPrototype()) {
// Prototype mode
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
// Custom Scope
String scopeName = mbd.getScope();
if(! StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally{ afterPrototypeCreation(beanName); }}); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); }catch (IllegalStateException ex) {
throw newScopeNotActiveException(beanName, scopeName, ex); }}}catch (BeansException ex) {
beanCreation.tag("exception", ex.getClass().toString());
beanCreation.tag("message", String.valueOf(ex.getMessage()));
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
finally {
// The bean creation is completebeanCreation.end(); }}// Check if required type matches the type of the actual bean instance.
if(requiredType ! =null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}
Copy the code
AbstractAutowireCapableBeanFactory
The figure shows that AbstractAutowireCapableBeanFacotry inherited AbstractBeanFactory, so in AbstractBeanFactory crateBean is an abstract method, While AbstractAutowireCapableBeanFacotry implements this crateBean abstract methods
createBean
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
MbdToUse is a reference to the MBD object
RootBeanDefinition mbdToUse = mbd;
// Parse the type of beanName, such as com.csh.baseimplClass<? > resolvedClass = resolveBeanClass(mbd, beanName);if(resolvedClass ! =null&&! mbd.hasBeanClass() && mbd.getBeanClassName() ! =null) {
/ / copy
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
// 2. Validate and prepare the override method (mark and validate the Override attribute)
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.
/ / resolveBeforeInstantiation method can return a proxy object, so as to achieve the effect of the "short circuit"
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 {
//Spring creates the Bean logic normally
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
resolveBeforeInstantiation
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
// Initialize bean=null
Object bean = null;
if(! Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// Make sure bean class is actually resolved at this point.
/ / MBD synthetic and BeanFacotry InstantiationAwareBeanPostProcessor interface
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<? > targetType = determineTargetType(beanName, mbd);if(targetType ! =null) {
/ / call applyBeanPostProcessorsBeforeInstantiation implementation agent
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if(bean ! =null) {
// Instantiated post-processor I have a vague feeling that AOP is involved herebean = applyBeanPostProcessorsAfterInitialization(bean, beanName); }}}// indicates that the bean has been parsed before being instantiatedmbd.beforeInstantiationResolved = (bean ! =null);
}
return bean;
}
Copy the code
applyBeanPostProcessorsBeforeInstantiation
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class
beanClass, String beanName) {
/ / InstantiationAwareBeanPostProcessor implementation class
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
//AOP dynamic proxy? I'm not sure
Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
// If result is not empty, the proxy behavior has occurred, and the return is a fully constructed bean, thus short-circuiting
if(result ! =null) {
returnresult; }}return null; } Let's go back to createBean and continue tracing the sourceCopy the code
doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
// The Bean wrapper class is null
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// If it is a factoryBean, remove it first to ensure a singleton
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// instantiate the bean and return the wrapper class BeanWarpper
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Get a reference to the native beanObject 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 {
@value, @autowire, @autowire, @value, @autowire, @value, @autowire, @value, @autowire
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.
// Determine if the bean needs to be exposed early (note that the bean is only instantiated, not initialized yet)
// Singleton and allows loop application of and the bean 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");
}
// In Bean and proxy object dependencies, this step gets the proxy,
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
// Record a reference to the previous native bean
Object exposedObject = bean;
try {
// Property population, loop dependencies should occur in this step
populateBean(beanName, mbd, instanceWrapper);
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); }}// If cyclic dependencies are allowed
if (earlySingletonExposure) {
// earlySingletonReference is not null only if the currently parsed bean has loop dependencies
Object earlySingletonReference = getSingleton(beanName, false);
if(earlySingletonReference ! =null) {
// If exposedObject is not enhanced in the initializeBean method, the previous loop is not affected
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// If enhanced, the beans on which the 'bean' depends are dirty data and need to be removed
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 {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
Copy the code
CreateBeanInstance method
Here is the actual bean instantiation without the agent
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.Class<? > beanClass = resolveBeanClass(mbd, beanName);// beanClass is not null && beanClass is not a public class (not a public modifier) && This bean does not allow access to non-public constructors and methods, and throws exceptions
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()); } Supplier<? > instanceSupplier = mbd.getInstanceSupplier();if(instanceSupplier ! =null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// The factory method is not empty, but is used to generate the bean
if(mbd.getFactoryMethodName() ! =null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
// Whether the factory method has been resolved
boolean resolved = false;
// Whether an auto-injected identifier is required
boolean autowireNecessary = false;
if (args == null) {
// Get lock thread safety
synchronized (mbd.constructorArgumentLock) {
// Check that the cache is not null and set the identifier
if(mbd.resolvedConstructorOrFactoryMethod ! =null) {
resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; }}}if (resolved) {
if (autowireNecessary) {
// If automatic injection is required, the auto-assembly constructor is called
return autowireConstructor(beanName, mbd, null.null);
}
else {
returninstantiateBean(beanName, mbd); }}// Candidate constructors for autowiring?
/ / rear application processor SmartInstantiationAwareBeanPostProcessor, get bean candidate constructorConstructor<? >[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if(ctors ! =null|| mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || ! ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if(ctors ! =null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
// Simple instantiation
return instantiateBean(beanName, mbd);
}
Copy the code
This bean instantiation process has ended, if interested in details of createBeanInstance, can access the Daniel link blog.csdn.net/v123411739/… The writer’s pen is limited.. I couldn’t keep up. AddSingletonFactory (beanName, () -> getEarlyBeanReference(beanName, MBD, bean)); Then invokes a getSinglton (this is what the author mentioned above, another getSingleton method of DefaultSingletonBeanRegistry) call in, so in line with the attitude of trivial pursuit, we naturally want to track the this method
The addSingltonFacotry has been analyzed in the DefaultSingletonRegistry and will not be repeated here. The main thing here is to look at the getEarlyBeanReference method
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
// getEarlyBeanReference is an interface rewritten by AbstractautoXyCreatorexposedObject = bp.getEarlyBeanReference(exposedObject, beanName); }}return exposedObject;
}
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// Return a proxy object or native bean, where AOP is involved
return wrapIfNecessary(bean, beanName, cacheKey);
}
Copy the code
The getSingleton method is the same as the getSingleton method I mentioned above. Note that the cyclic dependencies are performed recursively, assuming that Bean B is the dependency of Bean A. AddSingletonFactory (beanName, () -> getEarlyBeanReference(beanName, MBD, bean)); When Bean B calls getSingleton, the ObjectFactory of Bean A is removed from the level 3 cache and A Bean Aput is instantiated into the level 2 cache so that Bean B can successfully dependency inject A.
At this point, the logic in createBean is complete. Note that the outer layer (doGetBean) also has a getSingleton
public Object getSingleton(String beanName, ObjectFactory
singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
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 + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
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;
}
afterSingletonCreation(beanName);
}
if(newSingleton) { addSingleton(beanName, singletonObject); }}returnsingletonObject; }}Copy the code
The key, I think, is addSingleton. In A cyclic dependency scenario, B calls the addSingleton method before A does. This is different from the addSingletonFactory method mentioned above, which puts the second level cache into the third level cache, and the former method, which puts the entire Bean into the first level cache. Let’s now look at the addSingleton method
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName); }}Copy the code
Now that the entire SpringBean creation process (presumably, property population without analysis, initialization) and loop dependencies have been explained, let’s think about a few questions.
- In an AOP loop-dependent scenario, where the A object itself is initialized while the container and injected into B are proxy objects, wouldn’t that be A problem? No, this is because both cglib proxies and JDK dynamically generated proxy classes hold A reference to the target class internally. When A proxy object’s methods are called, the target object’s methods are actually called. When A initializes, the proxy object itself is initialized
Why is Spring level 3 cache designed this way
Author’s opinion: In fact, level 1 caching can solve the problem of loop dependency, but using level 1 caching can be much more complicated. After locking, parallelism becomes serial, so Spring designed level 2 caching. Level 2 caching improves concurrency efficiency, as can be seen from my analysis of getSingleton (the top one). As for the three-level cache, the three-level cache is actually designed to decouple Spring AOP from Spring IOC. If only the two-level cache is used, then Bean A will be proxied before injecting Bean B in the above example. After introducing the three-level cache, The time to complete an AOP proxy is when the Bean B dependency injection Bean A obtains the proxy or plain object through methods that implement the getEarlyReference interface