In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)05/31 Report--
This article mainly explains the "Spring circular dependency case analysis in Java". The content of the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "Spring circular dependency case analysis in Java".
What is circular dependency?
Quite simply, An object depends on B object, and B object depends on An object.
For example:
So is circular dependency a problem?
If you don't consider Spring, circular dependencies are not a problem, because it's normal for objects to depend on each other.
For example:
In this way, Amemery B depends on it.
However, circular dependency is a problem in Spring. Why? Because, in Spring, an object is not a simple new out, but will go through a series of Bean life cycle, it is because of the Bean life cycle that there will be circular dependency problems. Of course, there are many scenarios of circular dependency in Spring, some of which are solved automatically by Spring, while others need to be solved by programmers, which are described in more detail below.
To understand circular dependencies in Spring, you need to understand the lifecycle of Bean in Spring.
Life cycle of Bean
The life cycle of Bean is not described in detail here, only the general process is described.
The life cycle of Bean refers to: how is Bean generated in Spring?
The object managed by Spring is called Bean. The steps for generating Bean are as follows:
1. Spring scans class to get BeanDefinition
2. Generate bean according to the obtained BeanDefinition
3. First, infer the construction method according to class.
4. According to the inferred construction method, reflection, we get an object (temporarily called the original object).
5. Populate the attributes in the original object (dependency injection)
6. If a method in the original object is AOP, then a proxy object needs to be generated from the original object
7. Put the resulting proxy object into the singleton pool (called singletonObjects in the source code), and take it directly from the singleton pool the next time you getBean.
As you can see, there are many steps for the generation of Bean in Spring, and not only the above seven steps, but also many, such as Aware callback, initialization, and so on, which are not discussed in detail here.
As you can see, in Spring, constructing a Bean includes the step of new (step 4, constructor reflection). After getting an original object, Spring needs to do dependency injection on the attributes in the object, so what is the injection process?
For example, in class A mentioned above, there is a b attribute of class B in class A, so when class A generates an original object, it will assign a value to the b attribute. At this time, it will go to BeanFactory to obtain the singleton bean corresponding to class B according to the type and name of the b attribute. If there is a Bean corresponding to B in the BeanFactory, then assign a value to the b attribute directly; if there is no Bean corresponding to B in the BeanFactory, you need to generate a Bean corresponding to B, and then assign a value to the b attribute.
The problem arises in the second case, if class B has not yet generated the corresponding Bean in BeanFactory, then it needs to be generated and will go through the life cycle of B's Bean.
In the process of creating a Bean of class B, if there is an an attribute of class An in class B, then the corresponding Bean of class An is required in the process of creating the Bean of class B. however, the condition for triggering the creation of class B Bean is the dependency injection of class A Bean in the process of creation, so circular dependency occurs here:
ABean creation-> relies on B attribute-> triggers BBean creation-> B depends on An attribute-> requires ABean (but ABean is still in the process of being created)
As a result, the ABean cannot be created, and the BBean cannot be created.
This is the scenario of circular dependency, but as mentioned above, in Spring, there are some mechanisms to help developers solve the problem of partial circular dependency, which is three-level cache.
Three-level cache
Three-level cache is a general term.
The first level cache is: singletonObjects
The secondary cache is: earlySingletonObjects
The third level cache is: singletonFactories
First, explain the role of these three caches, which will be analyzed in detail later:
Cached in singletonObjects are bean objects that have gone through a full life cycle.
EarlySingletonObjects has one more early than singletonObjects, indicating that earlier bean objects are cached. What do you mean early? It means that the Bean has been put into the Bean before the end of its life cycle.
The cache in singletonFactories is ObjectFactory, which represents the object factory that is used to create an object.
Analysis of ideas to solve circular dependence
Let's first analyze why caching can solve circular dependencies.
According to the above analysis, the main reasons for circular dependency are:
When An is created-> B is needed to create-> B to create-> An is needed, resulting in a loop.
So how to break the cycle and add a middleman (cache)
During the creation of A's Bean, before dependency injection, the original Bean of An is put into the cache (exposed early, as long as it is in the cache, other Bean can be taken from the cache when needed), and then dependency injection is carried out. At this time, A's Bean depends on B's Bean. If B's Bean does not exist, you need to create B's Bean, and the process of creating B's Bean is the same as that of A. First, create an original object of B, then expose the original object of B early and put it in the cache, and then inject An into the original object of B, at this time, you can get the original object of A from the cache (although it is the original object of A, not the final Bean). After the injection of the original object of B, the life cycle of B ends, then the life cycle of A can also end.
Because there is only one A primitive object in the whole process, it does not matter for B, even if the A primitive object is injected during attribute injection, because the A primitive object does not change in the heap for the rest of the life cycle. As you can see from the above analysis, circular dependencies can be solved with only one cache, so why do you need singletonFactories in Spring?
This is difficult, based on the above scenario, think of a question: if the original object of An is injected into the attribute of B, and the original object of An is AOP to produce a proxy object, it will appear at this time. For A, its Bean object should be the proxy object after AOP, while the an attribute of B does not correspond to the proxy object after AOP, which leads to a conflict.
The An on which B depends and the final An are not the same object.
So how to solve this problem? It can be said that there is no solution to this problem.
Because at the end of the life cycle of a Bean, Spring provides BeanPostProcessor to process the Bean, which not only modifies the attribute value of the Bean, but also replaces the current Bean.
For example:
Run the main method and the resulting print is as follows:
So the bean object corresponding to a beanName can be completely replaced in BeanPostProcessor.
On the other hand, the execution of BeanPostProcessor is after attribute injection in the life cycle of Bean, and circular dependency occurs in the process of attribute injection, so it is likely that the An object injected into B object and the An object after the full life cycle are not an object. * * this is the problem.
So Spring can't solve the circular dependency in this case, because Spring doesn't know what BeanPostProcessor the An object will go through and what it will do to the An object when the attribute is injected.
In which case does Spring solve the circular dependency?
Although the above situation may happen, but it must be rare, we usually do not do this in the development process, but a beanName corresponding to the final object and the original object is not the same object will often occur, this is AOP.
AOP is implemented through a BeanPostProcessor, this BeanPostProcessor is AnnotationAwareAspectJAutoProxyCreator, its parent class is AbstractAutoProxyCreator, and in Spring AOP uses either JDK dynamic proxy or CGLib dynamic proxy, so if you set an aspect to a method in a class, then the class eventually needs to generate a proxy object.
The general process is: class A-> generate a normal object-> attribute injection-> generate a proxy object based on the aspect-> put the proxy object into the singletonObjects singleton pool.
AOP can be said to be another major function of Spring in addition to IOC, and circular dependency belongs to the category of IOC, so if these two functions want to coexist, Spring needs special treatment.
How to deal with it is to take advantage of the third-level cache singletonFactories.
First of all, the ObjectFactory corresponding to a beanName is stored in the singletonFactories, and after the original object is generated in the life cycle of the bean, an ObjectFactory is constructed and stored in the singletonFactories. This ObjectFactory is a functional interface, so the Lambda expression is supported: ()-> getEarlyBeanReference (beanName, mbd, bean)
The above Lambda expression is an ObjectFactory, and executing the Lambda expression will execute the getEarlyBeanReference method
The method is as follows:
This method executes the getEarlyBeanReference method in SmartInstantiationAwareBeanPostProcessor, but only two of the implementation classes under this interface implement this method, one is AbstractAutoProxyCreator and the other is InstantiationAwareBeanPostProcessorAdapter
Its implementation is as follows:
So obviously, in the entire Spring, by default only AbstractAutoProxyCreator actually implements the getEarlyBeanReference method, and this class is used for AOP. The parent class of the AnnotationAwareAspectJAutoProxyCreator mentioned above is AbstractAutoProxyCreator.
So what exactly is the getEarlyBeanReference method doing?
The first thing you get is a cachekey,cachekey, which is beanName.
Then save beanName and bean (which is the original object) into earlyProxyReferences and call wrapIfNecessary to AOP to get a proxy object.
So, when will the getEarlyBeanReference method be called? Go back to the scenario of circular dependency
Left text: this ObjectFactory is the labmda expression mentioned above, with the getEarlyBeanReference method in the middle. Note that the lambda expression will not be executed when you save the singletonFactories, that is, the getEarlyBeanReference method will not be executed.
Right text: get an ObjectFactory from singletonFactories according to beanName, and then execute ObjectFactory, that is, execute the getEarlyBeanReference method. At this time, you will get a proxy object after the original object A has passed through AOP, and then put the proxy object into the earlySingletonObjects. Note that the proxy object is not put into the singletonObjects at this time, so when will it be put into the singletonObjects?
At this time, we have to understand the role of earlySingletonObjects. At this time, we only get the proxy object of A's original object, which is not complete, because A's original object has not been filled with attributes, so we can't put A's proxy object directly into singletonObjects at this time, so we can only put the proxy object into earlySingletonObjects. Assuming that other objects now depend on A, we can get the proxy object of A's original object from earlySingletonObjects. And is the same proxy object of A.
When B is created, A continues its life cycle, and after completing attribute injection, A will AOP according to its own logic, and at this time we know that the original object A has already experienced AOP, so for An itself, it will no longer carry out AOP, so how can we tell whether an object has experienced AOP? The earlyProxyReferences mentioned above will be used. In the postProcessAfterInitialization method of AbstractAutoProxyCreator, it will determine whether the current beanName is in earlyProxyReferences. If so, it means that AOP has been performed in advance, and there is no need to AOP again.
As far as An is concerned, after the judgment of AOP and the execution of BeanPostProcessor, we need to put the corresponding object of An into singletonObjects, but we know that the proxy object of A should be put into singletonObjects, so we need to get the proxy object from earlySingletonObjects and put it into singletonObjects.
Thank you for your reading, the above is the content of "Spring circular dependency case analysis in Java". After the study of this article, I believe you have a deeper understanding of the problem of Spring circular dependency example analysis in Java, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.