In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-09 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)05/31 Report--
How to solve the problem of ineffective Spring transactions and circular dependence, I believe that many inexperienced people do not know what to do about this. Therefore, this paper summarizes the causes and solutions of the problem. Through this article, I hope you can solve this problem.
First, ask questions
I don't know if you have ever developed web references in the ssm framework or developed applications using springboot. When we call a method with @ Transactional annotations to perform a transaction operation, we sometimes find that the transaction is not valid.
Have you ever thought about why and how to fix things?
Second, analyze the problem
To understand why transactions do not work, we first need to understand how transactions are implemented in Spring, while declarative transactions in Spring are implemented using AOP.
What is the implementation of AOP in Spring? Dynamic proxy, two kinds of dynamic proxy used in Spring, one is JDK dynamic proxy provided by java natively, the other is CGLIB dynamic proxy provided by a third party, the former is based on interface implementation, the latter is based on class implementation, obviously the latter has a wider scope of application, but the native JDK dynamic proxy is much faster, both have their own characteristics.
The purpose of the dynamic agent is to generate the agent class in real time when the application is running, so that we can enhance it on the basis of the existing implementation, which is actually the purpose of AOP to enhance the function of the class.
The proxy class generated by the dynamic proxy has all the public methods of the native class, and the call to the specified method is transferred to the method of the proxy class with the same name, and within this method, some other operations are performed besides calling the method of the native class with the same name, such as logging, such as security checks, such as transaction operations, and so on.
When we directly call a method with transaction annotations in the service layer in the Controller layer, we perform the above steps: generate the proxy class, call the method of the proxy class with the same name, implement the transaction function by the proxy class, and then call the method of the native class for logical execution.
There is no problem with the above situation, but the problem is that when the method inside the service layer calls the method with transaction annotation in this class, the transaction annotation will become invalid. Our way of calling is nothing more than calling directly or using this. The effect of these two cases is actually the same, both of which are called with the current instance.
Combined with the previous introduction of AOP and dynamic proxy, we can easily understand the reason for the transaction failure here: that is, we call the native method directly when we call the target transaction method, instead of calling the proxy method in the proxy class, that is, we do not call the transaction-enhanced method, so the transaction will of course fail.
In this way, we need to call the enhanced proxy method in the proxy class for the transaction to take effect.
Third, solve the problem
So how do we fix it? It's really simple, as long as we don't use this calls. This represents the current instance, which is generally a singleton instance in spring. You call your own method, and the transaction annotation is equal to the device. If we change the invocation mode, inject our own singleton instance into the current class, and use the injected instance to call the method, we can make the transaction effective.
Why? Generally speaking, the Service layer in our SSM architecture has interfaces and implementation classes. Since there are interfaces, we must use JDK dynamic proxies to generate proxy classes. When we use the injected singleton instance to invoke methods in the interface after we have injected the singleton instance of the current class into ourselves, if there are AOP enhancement annotations such as @ Transactional, then we generate a proxy class to implement the enhancement. When developing in Springboot, we are used to removing interface development, so proxy classes are generated using CGLIB dynamic proxies.
This also requires that our transaction method be declared in the interface, then implement the logic in the implementation class, and add transaction annotations.
This approach is suitable for solving the problem of transaction invalidation when calling a transaction method in Service in Service. Come to think of it, the previous call to Service from Controller was also called through the injected singleton instance of Service, which also proved that the method we provided was effective.
IV. Extension of the question
4.1 extended problem: circular dependency
As for another question that arises from this: when we inject an instance of the current class into the current class, we need to inject an instance of this class when we create an instance of this class, but has the class been created at this time? what are we going to do?
This is the famous circular dependency problem in Spring.
A more obvious example is that if you rely on both B and B in An and depend on each other, will it cause both instances to fail?
4.2 Solutions for circular dependencies
It is necessary to briefly describe the solution to this problem in Spring. Why is it a brief introduction, because I only have a simple understanding, but this simple understanding is more applicable to friends who do not understand, so that they will not be confused at first.
We all know that Bean has a variety of lifecycle ranges in Spring, mainly singletons and prototypes (of course, request, Session, etc.). Singletons mean that only one Bean instance exists in the entire application context, while prototypes can have multiple Bean instances, and a new bean instance is created each time getBean is called.
We want to emphasize that if circular dependencies occur on prototype-scoped Bean instances in Spring, there is only one end: throwing an exception.
For the singleton bean,Spring, an effective mechanism of early exposure is provided to solve the problem of circular dependency. Of course, the only solution here is to use setter to implement dependency injection, if it is the case of using constructor dependency injection or that kind of end: throwing an exception.
Throwing an exception means that Spring does not have the ability to solve this problem and there is an error in the program.
Why? Doesn't Spring want to solve it? Definitely not, but there's nothing I can do.
Let's first take a brief look at the setter solution to implement the circular dependency of a singleton Bean with dependency injection:
Let's first introduce the cache pools in Spring:
SingletonObjects: singleton cache pool, which is used to save the created singleton Bean (Map). All the created Bean instances are saved in the cache pool, and the Bean without circular dependency will be saved to the cache directly after creation, while the bean with circular dependency will be transferred to this cache by earlySingletonObjects after its creation.
SingletonFactories: singleton factory cache pool, used to hold pre-exposed ObjectFactory, is Map
EarlySingletonObjects: the early singleton cache pool, which is used to hold the singleton Bean that has not yet been created for early exposure, is Map, which is mutually exclusive with singletonObjects, but cannot be saved at the same time, but can only be saved alternatively. The Bean instance that is not yet created and injected into other Bean can be said to be an intermediate cache (or process cache). Only when the native Bean corresponding to the BeanName (in the pool under creation) is injected into another bean instance, it is added to the cache. The semi-finished bean instance is always stored in this cache. When the Bean instance is finally created, it will be removed from this cache and transferred to the singletonObjects cache for storage.
RegisteredSingletons: registered singleton cache pool, used to hold the beanName of the Bean instance that has been created, is Set (this cache is not involved)
SingletonsCurrentlyInCreation: create the pool and save the BeanName of the singleton bean under creation, which is Set. It is added to the pool when the bean instance is created, and removed from the pool after the Bean instance is created.
When there are circular dependencies, such as the previous case where A depends on both B and A, first create An instance, add its beanName to the singletonsCurrentlyInCreation pool, then call A's constructor to create a native instance of A, add its ObjectFactory to the singletonFactories cache, then deal with dependency injection (B instance), and find that B instance does not exist and does not exist in the singletonsCurrentlyInCreation pool Indicates that the Bean instance has not been created yet, so the next step is to create instance B, add its beanName to the singletonsCurrentlyInCreation pool, then call the constructor of B to create the native instance of A, add its ObjectFactory to the singletonFactories cache, and then deal with dependency injection (An instance). It is found that instance A has not been created yet, but the beanName of instance An is found in the singletonsCurrentlyInCreation pool, indicating that instance An is in the process of being created. Spring will inject into B the Bean instance returned by the getObject method in the ObjectFactory of the corresponding A's beanName in the singletonFactories cache to complete the creation of the B instance. At the same time, it will also add the A's Bean instance to the earlySingletonObjects cache, indicating that the An instance is a pre-exposed Bean instance. After the B instance is created, the native instance of B needs to be removed from the singletonFactories cache, and the complete instance needs to be added to the SingletonObjects cache (of course, earlySingletonObjects cannot exist). And remove its beanName from the singletonsCurrentlyInCreation pool (indicating that the B instance is fully created). Then inject the B instance into the An instance to complete the creation of the An instance. Finally, remove the native instance of A from the earlySingletonObjects, add the complete instance to the SingletonObjects, and remove the beanName of A from the pool under creation. At this point, the creation of two singleton instances An and B is completed.
After understanding the solution described above, we can understand why circular dependencies cannot be resolved in the case of circular dependencies on Bean that implement dependency injection for constructors. That is because the premise previously exposed in advance is to create the original Bean instance. The original Bean instance is created by the constructor. If the constructor needs to inject the dependency when creating the Bean, and the dependency is being created, the circular dependency problem cannot be solved because the ObjectFactory cannot be exposed.
In addition, in the case of the prototype bean, Spring will not add cache to the prototype Bean at all, because the purpose of adding the cache is to ensure the uniqueness of the singleton Bean, but for the prototype, it cannot be cached. If the Bean instance is obtained from the cache, is it still the prototype pattern? Without caching, of course, the series of operations described above cannot be implemented, and the problem of circular dependency cannot be solved.
The transaction problem in Spring boils down to the injection problem, and the circular dependency problem is also the injection problem, which will be discussed later.
All the enhancements in Spring rely on AOP, while AOP relies on dynamic proxy. The dynamic proxy of JDK depends on reflection technology, while the dynamic proxy of CGLIB depends on bytecode technology.
After reading the above, do you know how to solve the problem of invalid Spring transactions and circular dependency? If you want to learn more skills or want to know more about it, you are welcome to follow the industry information channel, thank you for reading!
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.