Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The solution of cyclic dependence in Spring

2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces "the solution of circular dependency in Spring". In the daily operation, I believe that many people have doubts about the solution of circular dependency in Spring. The editor consulted all kinds of data and sorted out simple and easy-to-use operation methods. I hope it will be helpful for you to answer the doubt of "the solution of circular dependency in Spring". Next, please follow the editor to study!

First, what are the circular dependencies solved by Spring?

There are two kinds of circular dependencies in Java, one is the circular dependency of the constructor, the other is the circular dependency of attributes.

The circular dependency of a constructor is that there is an attribute circular dependency in the constructor, and the two classes shown below belong to the constructor circular dependency:

@ Servicepublic class Student {@ Autowired private Teacher teacher; public Student (Teacher teacher) {System.out.println ("Student init1:" + teacher);} public void learn () {System.out.println ("Student learn");}}

@ Servicepublic class Teacher {@ Autowired private Student student; public Teacher (Student student) {System.out.println ("Teacher init1:" + student);} public void teach () {System.out.println ("teach:"); student.learn ();}}

There is no solution to this circular dependency, because when the JVM virtual machine instantiates a class, it needs to instantiate the constructor parameter first, and because the circular reference parameter cannot be instantiated in advance, it can only throw an error.

The circular dependency solved by Spring refers to the circular dependency of an attribute, as shown below:

@ Servicepublic class Teacher {@ Autowired private Student student; public Teacher () {System.out.println ("Teacher init1:" + student);} public void teach () {System.out.println ("teach:"); student.learn ();}}

@ Servicepublic class Student {@ Autowired private Teacher teacher; public Student () {System.out.println ("Student init:" + teacher);} public void learn () {System.out.println ("Student learn");}}

Test scan class:

@ ComponentScan (value = "myPackage") public class ScanConfig {}

Test the startup class:

Public class SpringTest {public static void main (String [] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext (ScanConfig.class); applicationContext.getBean (Teacher.class). Teach ();}}

Test class execution results:

Student init:null Teacher init:null teach: Student learn

As you can see, the injection of the property is not completed when the constructor executes, but the injection is completed when the method is called. Let's take a look at when attribute injection is done internally in Spring and how to solve circular dependencies.

Circular dependency and attribute injection

1. For non-lazily loaded classes, the package scan is completed by the finishBeanFactoryInitialization (beanFactory) method in the refresh method, as well as the initialization of bean, which will be tracked together.

Protected void finishBeanFactoryInitialization (ConfigurableListableBeanFactory beanFactory) {/ / other code / / Instantiate all remaining (non-lazy-init) singletons. BeanFactory.preInstantiateSingletons ();}

You can see that a method of beanFactory is called, and the beanFactory here refers to our most common DefaultListableBeanFactory. Let's take a look at the method in it.

2. PreInstantiateSingletons method of DefaultListableBeanFactory

Public void preInstantiateSingletons () throws BeansException {List beanNames = new ArrayList (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 ()) {/ / judged to be a non-abstract class, singleton, non-lazy loading to initialize if (isFactoryBean (beanName)) {/ / irrelevant code (processing for FactoryBean)} else {/ / important! Ordinary bean is the getBean (beanName) initialized here;} / / other extraneous code}

As you can see, it is in this method that all the bean in the Spring container is looped and initialized in turn. The entry for initialization is the getBean method.

3. GetBean and doGetBean methods of AbstractBeanFactory

Trace the getBean method:

Public Object getBean (String name) throws BeansException {return doGetBean (name, null, null, false);}

It can be seen that the overloaded doGetBean method is referenced and continue to trace it:

Protected T doGetBean (final String name, @ Nullable final Class requiredType, @ Nullable final Object [] args, boolean typeCheckOnly) throws BeansException {final String beanName = transformedBeanName (name); Object bean; / / method 1) gets the singleton class Object sharedInstance = getSingleton (beanName) from three map; / / omits the irrelevant code} else {/ / if it is a circular reference of multiple instances, it directly reports an error if (isPrototypeCurrentlyInCreation (beanName)) {throw new BeanCurrentlyInCreationException (beanName) } / / omit some extraneous codes try {/ / Create bean instance. If (mbd.isSingleton ()) {/ / method 2) get the singleton object sharedInstance = getSingleton (beanName, ()-> {try {/ / method 3) create the return value return createBean (beanName, mbd, args) of the getObject method in ObjectFactory;} 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); throw ex;}}); bean = getObjectForBeanInstance (sharedInstance, name, beanName, mbd);}} / omit some extraneous codes return (T) bean;}

This method is relatively long, and the three methods marked above play a vital role in solving circular references, so let's conquer them one by one.

3) getSingleton (beanName) method: note that this method is overloaded with the same name but different internal logic as method 2).

Protected Object getSingleton (String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get (beanName); / / step An if (singletonObject = = null & & isSingletonCurrentlyInCreation (beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get (beanName); / / step B if (singletonObject = = null & & allowEarlyReference) {ObjectFactory singletonFactory = this.singletonFactories.get (beanName); / / step C if (singletonFactory! = null) {singletonObject = singletonFactory.getObject (); this.earlySingletonObjects.put (beanName, singletonObject) This.singletonFactories.remove (beanName);} return singletonObject;}

The priority of these three map can be seen from the steps above. The singleton object after initialization is stored in singletonObjects; the early singleton object that has completed instantiation but not initialization is stored in earlySingletonObjects; and the ObjectFactory object is stored in singletonFactories. The getObject method return value of this object is the singleton object that has just been instantiated and not yet initialized. So the order is that singleton objects first exist in singletonFactories, then in earlySingletonObjects, and finally put into singletonObjects after initialization.

When debug comes here, take the classes referenced by the above two loops, Teacher and Student, as an example. If the first one to do this is Teacher, then the values get to in the three map from here are empty because they have not been added yet. This method is mainly used for objects that come later in circular dependencies.

3.2) getSingleton (String beanName, ObjectFactory singletonFactory) method:

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) {/ / omit extraneous code beforeSingletonCreation (beanName); / / step A boolean newSingleton = false; / / omit extraneous code try {singletonObject = singletonFactory.getObject (); / / step B newSingleton = true } / / omit extraneous code finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;} afterSingletonCreation (beanName); / / step C} if (newSingleton) {addSingleton (beanName, singletonObject); / / step D}} return singletonObject;}}

The main logic of getting a singleton object is implemented by this method, which is mainly divided into the above four steps. Continue to look at them one by one:

Step A:

Protected void beforeSingletonCreation (String beanName) {/ / judge, and put beanName that is teacher in singletonsCurrentlyInCreation for the first time if (! this.inCreationCheckExclusions.contains (beanName) & &! this.singletonsCurrentlyInCreation.add (beanName)) {throw new BeanCurrentlyInCreationException (beanName);}}

Step C:

After protected void afterSingletonCreation (String beanName) {/ / gets the singleton object, beanName removes if (! this.inCreationCheckExclusions.contains (beanName) & &! this.singletonsCurrentlyInCreation.remove (beanName)) {throw new IllegalStateException ("Singleton'" + beanName + "'isn't currently in creation");}}

Step D:

Protected void addSingleton (String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put (beanName, singletonObject); / add singletons to map this.singletonFactories.remove (beanName); / / remove from early exposed factories, this map plays a key role in resolving circular dependencies this.earlySingletonObjects.remove (beanName); / / remove this.registeredSingletons.add (beanName) from early exposed object map / / add to the registered singleton name collection}}

Step B:

The getObject method of ObjectFactory is called here. Where is this method implemented? What is returned? And turn back, find method 3 in 3, those who have known about java8 functional programming should be able to see that the return value of method 3 [createBean (beanName, mbd, args)] is the return value of getObject method, that is, method 3 returns the singleton object we need, and the following goes after tracking method 3.

3) AbstractAutowireCapableBeanFactory#createBean (java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object []) method

Protected Object createBean (String beanName, RootBeanDefinition mbd, @ Nullable Object [] args) throws BeanCreationException {/ / omit extraneous codes try {Object beanInstance = doCreateBean (beanName, mbdToUse, args); return beanInstance;} / / omit extraneous codes}

After removing the extraneous code, the key method is the doCreateBean method, which is tracked:

Protected Object doCreateBean (final String beanName, final RootBeanDefinition mbd, final @ Nullable Object [] args) throws BeanCreationException {BeanWrapper instanceWrapper = null; / / omit code if (instanceWrapper = = null) {/ / instantiate bean instanceWrapper = createBeanInstance (beanName, mbd, args);} boolean earlySingletonExposure = (mbd.isSingleton () & & this.allowCircularReferences & isSingletonCurrentlyInCreation (beanName)); if (earlySingletonExposure) {/ / focus! Add instantiated objects to singletonFactories addSingletonFactory (beanName, ()-> getEarlyBeanReference (beanName, mbd, bean));} / / initialize bean Object exposedObject = bean; try {populateBean (beanName, mbd, instanceWrapper); / / also important exposedObject = initializeBean (beanName, exposedObject, mbd);} / / omit extraneous code return exposedObject;}

The emphasis marked in the comments above is the key to this approach. In the addSingletonFactory method, the second parameter ObjectFactory is stored in singletonFactories to be called when other objects depend on it. Then the following populateBean method carries on the attribute injection to the newly instantiated bean (this method is more related, this article does not expand the tracking for the time being, interested gardeners can check it by themselves), if you encounter the object properties in the Spring, then use the getBean method to obtain the object. At this point, the processing of circular dependencies in Spring has been traced back, so let's summarize it.

Summary

Property injection is mainly done in the populateBean method. For circular dependencies, take the injection of Student into Teacher and the injection of Teacher into Student as an example, assume that the loading order of Spring is to load Teacher first, and then load Student.

After the initialization of Teacher is triggered by the getBean method:

a. First go to method 1 in 3). At this time, the map is empty and no instance can be obtained.

b. Then go to method 2), step A, step C, step D is the method of controlling the data in map, which is simple to implement and can be ignored for the time being. Where the getObject method of step B triggers the call to method 3)

c. In method 3), first instantiate the Teacher object through createBeanInstance, and then put the instantiated object into singletonFactories through the addSingletonFactory method to complete the early exposure of the Teacher object.

d. Then, in method 3), the property of the Teacher object is injected through the populateBean method, and it is found that it has a Student property, then the getBean method is triggered to initialize the Student.

e. Repeat steps a, b, c, except that the Student object is to be initialized at this time

f. When you get to d, call populateBean to inject attributes into the Student object and find that it has a Teacher property, then trigger the getBean method to initialize the Teacher

g. Initialize Teacher and return to a, but at this time map is no longer empty, because the Teacher instance has been put into singletonFactories in step c, and the Teacher instance is returned from a

h. Complete the initialization of Student in f, and then go back up in turn to complete the initialization of Teacher

After initializing the Teacher, initializing the Student is simple because the singleton is already stored in the map.

At this point, the summary and analysis of Spring circular dependency is over. In one sentence, Spring solves the problem of circular dependency by exposing the instantiated object to the singletonFactories in the Spring container in advance.

At this point, the study of "the solution to circular dependency in Spring" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report