Remember from the previous note that during the bean load process, during the creation process, a dependency loop was detected, and if the loop dependency is present and not resolved, the code will report an error and the Spring container initialization will fail.

Since sense loop dependency is a relatively independent topic, I will write a separate note on its analysis to look at what loop dependency is and how to solve it.


Circular dependencies

Circular dependencies are circular references, where two or more beans hold each other to form a ring. For example, A references B, B references C, and C references A.

This can be understood by looking at the following example (the dependency of classes is shown here, but circular calls refer to ring calls between methods, as shown in the following code example) :

If you have learned database, you can simply understand the circular dependency as a deadlock, holding each other’s resources, forming a ring, and then do not release resources, resulting in deadlock.

In a loop call, unless a terminating condition occurs, the loop will continue indefinitely, resulting in an out-of-memory error. (I also encountered OOM once, also caused by infinite loop)


The example in the book uses three classes for ring calls. For simple understanding and demonstration, I use two classes for ring calls:

In Spring, circular dependencies fall into three categories:

Constructor loop dependencies

Through the above configuration method, at the time of initialization will be thrown BeanCurrentlyInCreationException anomalies

public static void main(String[] args) {
	Requested bean is currently in creation: is there an unresolvable circular reference?
	ApplicationContext context = new ClassPathXmlApplicationContext("circle/circle.xml");
}
Copy the code

Know from the previous notes, the Spring container will each and every one is creating bean identifier in a “current to create large pools of bean (prototypesCurrentlyInCreation)”, the bean identifiers will remain in the creation process in the pool.

Method of detecting cyclic dependencies:

In this example, when you instantiate circleA, you put A into the pool, and because you depend on circleB, you de-instantiate circleB, and then you put B into the pool, and because you depend on A, you want to instantiate A, Found in the process of creating bean has found itself in the “current create bean”, so will be thrown BeanCurrentlyInCreationException anomalies.

As shown in the figure, this circular dependency, injected through the constructor, is irresolvable.


Prototype scope dependency handling

Prototype is a scope, so let’s look at the concept of scope:

inSpringContainer, in the Spring container, is its creationBeanObject relative to otherBeanObject request visibility scope

We most commonly use singleton scoped beans. There will only be one shared bean instance in the Spring container, so we will only return the same instance of the bean every time we get the same ID.

The benefits of using singletons are twofold:

  1. Advance instantiationbeanTo expose problematic configuration problems in advance
  2. willbeanInstances are placed in the singleton cachesingletonFactories, when the need to use again, directly from the cache, speed up the operation efficiency.

Single instances are stored in the singleton cachesingletonFactoriesIs the default scope of Spring.

Having looked at the singleton scope, let’s look at the concept of the PropoType scope: When Spring calls the prototype bean, it returns a new Object each time, equivalent to a new Object().

becauseSpringContainer to stereotype scopedbeanIs not cached and therefore cannot expose a creation in advancebean, so it is also unable to solve this case of cyclic dependency.


Setter loop dependency

Dependencies on setter injection can be accomplished by the Spring container by pre-exposing beans that have just completed constructor injection but have not completed other steps (such as setter injection), and only singleton scoped bean dependencies can be resolved.

In class loading, the core method org. Springframework. Beans. Factory. Support. AbstractAutowireCapableBeanFactory# doCreateBean, in this step is to check and deal with circular dependencies.

Following through, you can see that if the bean is a singleton and allows circular dependencies, you can finally solve the problem of circular dependencies by exposing a singleton factory method ahead of time so that other beans can reference it.

So let’s do setter solutions for CircleA and CircleB:

Configuration:

<! -- Comment 5.3 Setter method injection -->
<bean id="circleA" class="base.circle.CircleA">
	<property name="circleB" ref="circleB"/>
</bean>

<bean id="circleB" class="base.circle.CircleB">
	<property name="circleA" ref="circleA"/>
</bean>
Copy the code

Execute Demo and output:

public static void main(String[] args) {
	ApplicationContext context = new ClassPathXmlApplicationContext("circle/circle.xml");
	CircleA circleA = (CircleA) context.getBean("circleA"); circleA.a(); } in method A, output a, in method B, output b, the following is the result of executing demo output: Java.lang. Instrument ASSERTION FAILED =-= A B A B *** Java.lang. Instrument ASSERTION FAILED ***:! "" errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: ! "" errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: ! "" errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
Exception in thread "main" java.lang.StackOverflowError
Copy the code

Setsetinjection: setsetinjection: setsetinjection: setinjection: setinjection: setinjection: setinjection: setinjection: setinjection: setinjection: setinjection: setinjection: setinjection


The code analysis

To better understand loop dependencies, let’s first look at the meaning and purpose of these three variables (also called caches, which can be called globally) :

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private finalMap<String, ObjectFactory<? >> singletonFactories =new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
Copy the code
variable use
singletonObjects Used to holdBeanNameAnd the creation ofbeanRelationships between instances,bean-name –> instanct
singletonFactories Used to holdBeanNameAnd the creation ofbeanThe factoryThe relationship between,bean-name –> objectFactory
earlySingletonObjects Also savebeanNameAnd the creation ofbeanRelationships between instances, andsingletonObjectsThe difference is, when a singletonbeanAfter being put in here, then the otherbeanDuring the creation process, it passesgetBeanMethod to obtain,The purpose is to detect circular references

Now that we’ve talked about the mechanics of class loading, let’s look at where loop dependencies are addressed when creating beans:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

// Whether the exposure needs to be advanced, which is used to solve the cycle dependence
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");
	}
	The second parameter is the callback interface, which dynamically weaves facets into the bean
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

protected void addSingletonFactory(String beanName, ObjectFactory
        singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
		// singletonObjects does not have a beanName
		if (!this.singletonObjects.containsKey(beanName)) {
		BeanName -> beanFactory = beanName -> beanFactory = beanName -> beanFactory
		this.singletonFactories.put(beanName, singletonFactory);
		// Remove from the pre-exposure cache, previously placed on getSingleton()
		this.earlySingletonObjects.remove(beanName);
		// Add beanName to the registration cache
		this.registeredSingletons.add(beanName); }}}Copy the code

Let’s start with the variable earlySingletonExposure: literally a single case that requires advance exposure.

There are the following three judgment conditions:

  • mbdIs it a singleton
  • Whether the container allows cyclic dependencies
  • Determine whether thebeanWhether the vm is being created.

If all three conditions are met, the addSingletonFactory operation is executed. Keep in mind that everything you write is useful, so let’s see what problem this action solves and where it is used


To solve the scene

Use the CircleA and CircleB loops referenced in the initial creation as examples:

Class A contains attribute B, and class B contains attribute A. Both classes go through the following steps when initializing:

  1. createbeanA, first record the correspondingbeanNamethenbeanACreate a beanFactoryAPut it in cache
  2. right beanAProperty fill method ofpopulateBeanDependencies are checkedbeanBNot in the cachebeanBInstance or singleton cache, so instantiatebeanB.
  3. Start instantiatingbeanB, experience creationbeanATo the property fill method, the dependency is checkedbeanA.
  4. callgetBean(A)Methods, in this function, are not really de-instantiatedbeanAInstead, it checks to see if there are already created corresponding ones in the cachebean, or already createdbeanFactory
  5. detectedbeanFactoryAIt’s already created. It’s called directlyObjectFactoryTo createbeanA

Combing process with key code

Creating the original bean

BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
/ / the original beans
final Object bean = instanceWrapper.getWrappedInstance();
Copy the code

In this step, the raw bean is created. Since we haven’t gotten to the last property resolution step, there are no property values in this class. Think of it as new ClassA, and there are no constructors or other assignments.


addSingleFactory

The second parameter is the callback interface, which dynamically weaves facets into the bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
Copy the code

This method, also mentioned earlier, adds singletons to the cache that need to be exposed ahead of time, and the beanName and beanFactory singletons to the cache, which will be taken out of the cache when needed later.


PopulateBean populates the property

As mentioned earlier in the first step, the initial bean is created with no attribute values, so the class attributes are resolved in this step. During attribute parsing, the type of the attribute is determined, and if it is determined to be a RuntimeBeanReference type, the reference is resolved.

As in the example we wrote, CircleA refers to CircleB, and when loading CircleA, we find that CircleB depends on it, so we need to load CircleB.

Let’s look at the flow in the code:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {...if(pvs ! =null) {
		// Apply the attributes to the bean, using a deep copy, copying the attributes of the subclassapplyPropertyValues(beanName, mbd, bw, pvs); }}protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {... String propertyName = pv.getName(); Object originalValue = pv.getValue();// Comment 5.5 parsing parameters, if reference objects, will be preloadedObject resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); . }public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
	// We have to check each value to see if it needs a runtime reference, and then parse the other bean
	if (value instanceof RuntimeBeanReference) {
		// Comment 5.6 In this step, if it is determined to bea reference type, you need to parse the reference and load another bean
		RuntimeBeanReference ref = (RuntimeBeanReference) value;
		returnresolveReference(argName, ref); }... }Copy the code

Now that I’ve traced it to this point, the process for loading a reference is a little clearer, and if I find that it’s a reference class, Will eventually appointed org. Springframework. Beans. Factory. Support. BeanDefinitionValueResolver# resolveReference reference processing, the core of the two lines of code is as follows:

// Comment 5.7 Load the referenced bean here
bean = this.beanFactory.getBean(refName);
this.beanFactory.registerDependentBean(refName, this.beanName);
Copy the code

So we’re loading CircleB in this step, but in the example we wrote, CircleB depends on CircleA, so how does it handle that, so at this point, the information that we just put CircleA into the cache is going to do the trick.


getSingleton

Remember that the singleton mode is used to fetch the same object every time the class is loaded. If it is in the cache, it can be fetched directly. If it is not in the cache, it can be loaded.

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	// Check if there is an instance in the cache
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		// Remember that all public variables need to be locked to avoid concurrent modification by multiple threads
		synchronized (this.singletonObjects) {
			// No processing if the bean is being loaded
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				// When some methods need to be pre-initialized, calling the addSingletonFactory method will be the corresponding
				// The objectFactory initialization policy is stored in earlySingletonObjects and removed from singletonFactoriesObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
				if(singletonFactory ! =null) {
					singletonObject = singletonFactory.getObject();
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName); }}}}return singletonObject;
}
Copy the code

Although CircleB references CircleA, CircleA’s beanFactory was exposed in advance when the previous method addSingletonFactory was used.

So CircleB gets the CircleA information when it gets the singleton getSingleton(), so it loads the CircleB information, adds its information to the cache and registry, and then goes back to load CircleA again. Since its dependencies are already loaded into the cache, CircleA is able to complete the load as well, and the entire load operation is complete

Combined with the flow chart and key code flow to solve the scene, a more complete introduction to the cycle dependency processing method, there is anotherdebugFlow chart, hope to deepen your understanding ~


conclusion

The purpose of this summary is to fill in the holes, because the previous article on class loading briefly covered the concept of cyclic dependencies and wanted to fill in the holes left in class loading.

Found in the process of analysis of circular dependencies, before do not understand scope of scope, then added to the knowledge points, and then find use in circular dependencies to the cache and detail processing is not familiar with, then turn to the related information, tracking source, step by step, so to write more and more found, solved a confused, added a few questions, Therefore, I deepened my understanding of Spring through continuous investigation and understanding.

Similarly, in the work, often in collaboration with other team, will meet at the same time to the other side of the new interface support, for example in the RPC encountered in the call, then I suggest or in a package, for example through the message decoupling, avoid cycle call, there is no way to call, remember to add exit condition in the method, to avoid an infinite loop (> _ <)


Due to limited personal skills, if there is any misunderstanding or mistake, please leave a comment, and I will correct it according to my friends’ suggestions

Spring-analysis-note cloud Gitee address

Spring – analysis – note making address


The resources

  1. Introduction to the five scopes of Spring beans
  2. Source code analysis of the Spring IOC container – a solution to loop dependencies
  3. Spring source code in-depth analysis – Hao Jia

Portal:

  • Spring source learning – environment preparation

  • (1) The infrastructure of the container

  • Spring source code learning (2) default tag parsing

  • Spring source code learning (3) custom tags

  • Spring source code learning (four) bean loading

  • Spring source code learning (5) loop dependency

  • Spring source code learning (six) extension function part 1

  • Spring source code learning (seven) extension features part 2

  • Spring source learning (eight) AOP use and implementation principle

  • Spring source code learning (9) Transaction Transaction

  • (10) Spring MVC