In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
How to solve the problem that another method transaction does not take effect in this class of Spring service, I believe many inexperienced people do not know what to do about it. Therefore, this paper summarizes the causes and solutions of the problem. Through this article, I hope you can solve this problem.
A few days ago, a friend needed to make a self-call in the target object, and needed to implement the corresponding transaction definition, and there was a problem with a solution through BeanPostProcessor on the Internet.
1. Preliminary knowledge
The method call after using the AOP proxy executes the process, as shown in the figure
In other words, we first call the AOP proxy object instead of the target object, first execute the transaction aspect, and the interior of the transaction aspect is enhanced by TransactionInterceptor surround enhancement, that is, to open the transaction before entering the target method and commit / roll back the transaction when exiting the target method.
2. Test code preparation
Java code
Public interface AService {public void a (); public void b ();} @ Service () public class AServiceImpl1 implements AService {@ Transactional (propagation = Propagation.REQUIRED) public void a () {this.b ();} @ Transactional (propagation = Propagation.REQUIRES_NEW) public void b () {}}
# 3. Question
Self-invocation within the target object will not be able to implement the enhancements in the aspect, as shown in the figure
The this here points to the target object, so calling this.b () will not perform the b transaction aspect, that is, transaction enhancement will not be performed, so the transaction definition "@ Transactional (propagation = Propagation.REQUIRES_NEW)" of the b method will not be implemented, that is, the result is that the transaction definitions of the b and a methods are the same, as you can see from the following log:
Org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method'a 'with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT;' 'org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean' txManager' org.springframework.orm.hibernate4.HibernateTransactionManager Creating new transaction with name [com.sishuok.service.impl.AServiceImpl1.a]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT;''- create a method transaction org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session... For Hibernate transaction-Open Session Org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl1.a] org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl1.a]-complete a method transaction org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit
Org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session... -commit a method transaction or org.springframework.orm.hibernate4.HibernateTransactionManager Rolling back Hibernate transaction on Session... -if there is an exception, the a method transaction will be rolled back
Org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization... Org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session... After transaction-close Session
We can see that the transaction aspect only enhances the a method, not the b method.
3. Solution
When method b is called in method a here, transaction enhancement can be done simply by calling method b through the AOP proxy, as shown below:
Java code
Public void a () {aopProxy.b (); / / that is, calling the b method of the AOP proxy object can execute the transaction aspect for transaction enhancement}
There are three ways to determine whether a Bean is an AOP proxy object: AopUtils.isAopProxy (bean): whether it is a proxy object; AopUtils.isCglibProxy (bean): whether it is a proxy object of CGLIB mode; and AopUtils.isJdkDynamicProxy (bean): whether it is a proxy object of JDK dynamic proxy mode.
Expose Aop proxy objects through ThreadLocal
1. Enable the support for exposing Aop proxies to ThreadLocal (starting from spring3 in the following configuration)
2. Modify our business implementation class
This.b ();-modified to-> ((AService) AopContext.currentProxy ()) .b ()
3. Execute the test case. The log is as follows
Org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean 'txManager' org.springframework.orm.hibernate4.HibernateTransactionManager Creating new transaction with name [com.sishuok.service.impl.AServiceImpl2.a]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT;''- create a method transaction org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session... For Hibernate transaction-- Open a Session org.springframework.orm.hibernate4.HibernateTransactionManager Preparing JDBC Connection of Hibernate Session... Org.springframework.orm.hibernate4.HibernateTransactionManager Exposing Hibernate transaction as JDBC transaction... …… Org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.a]
Org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method 'b'with attribute: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT;'. Org.springframework.orm.hibernate4.HibernateTransactionManager Suspending current transaction, creating new transaction with name [com.sishuok.service.impl.AServiceImpl2.b]-create a b method transaction (and pause a method transaction)... Org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session for Hibernate transaction-Open b Session. Org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.b] org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl2.b]-complete b method transaction
Org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session... -commit b method transaction org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization... Org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session... After transaction-close b Session
-at this point, the b method transaction is complete
Org.springframework.orm.hibernate4.HibernateTransactionManager Resuming suspended transaction after completion of inner transaction-resume a method transaction. Org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl2.a]-complete a method transaction org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session... -commit a method transaction org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization... Org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session... After transaction-close a Session
Here we can see that the transaction of the b method works.
The above is the simplest solution to solve the problem of self-invocation of internal methods of the target object and implementation of transactions.
4. Analysis of implementation principle.
4.1.After entering the proxy object, expose the current proxy object to ThreadLocal through AopContext.serCurrentProxy (proxy), and save the last ThreadLocal bound proxy object as oldProxy
Next, we can get the current proxy object through AopContext.currentProxy ()
Before exiting the proxy object, you should re-set the proxy object bound by ThreadLocal to the last proxy object, that is, AopContext.serCurrentProxy (oldProxy).
Some people don't like this approach, saying that there are performance problems exposed through ThreadLocal, but this doesn't need to be considered, because transaction-related (Session and Connection) internals are also exposed to ThreadLocal through SessionHolder and ConnectionHolder.
However, the scenario of self-invocation is really rare, so we can do it in the following ways without using this way.
3.2. Inject a proxy object into the target object through the initialization method
Java code
@ Service public class AServiceImpl3 implements AService {@ Autowired / / ① injection context private ApplicationContext context; private AService proxySelf / / ② represents the proxy object, not the target object @ PostConstruct / / ③ initialization method private void setSelf () {/ / get the proxy object from the context (if it is wrong through proxtSelf=this, this is the target object) / / this method is not suitable for prototype Bean, because each time getBean returns a new Bean proxySelf = context.getBean (AService.class) } @ Transactional (propagation = Propagation.REQUIRED) public void a () {proxySelf.b (); / ④ calls the method of the proxy object so that you can execute the transaction aspect} @ Transactional (propagation = Propagation.REQUIRES_NEW) public void b () {}}
The log is not analyzed here, similar to 3.1. This approach is not very flexible, and all implementation classes that need to be called by themselves must repeat the implementation code.
3.3.Inject a proxy object into the target object through BeanPostProcessor
You can refer to http://fyting.iteye.com/blog/109236 for this solution.
Please wait for my next analysis post on the introduction and use of BeanPostProcessor.
First, define the identification interface that BeanPostProcessor needs to use.
Java code
Public interface BeanSelfAware {void setSelf (Object proxyBean);}
That is, if our custom BeanPostProcessor (InjectBeanSelfProcessor) finds that our Bean implements the identification interface, it will call the setSelf injection proxy object.
2. Bean implementation
Java code
@ Service public class AServiceImpl4 implements AService, BeanSelfAware {/ / omit interface definition private AService proxySelf; public void setSelf (Object proxyBean) {/ / AOP proxy object this.proxySelf = (AService) proxyBean;} @ Transactional (propagation = Propagation.REQUIRED) public void a () {proxySelf.b () injected into oneself (target object) through InjectBeanSelfProcessor / / call the method of the proxy object so that you can execute the transaction aspect} @ Transactional (propagation = Propagation.REQUIRES_NEW) public void b () {}}
The setSelf that implements the BeanSelfAware identity interface injects the proxy object, and the transaction definition of the b method can be implemented through "proxySelf.b ()".
Third, InjectBeanSelfProcessor implementation
Java code
@ Component public class InjectBeanSelfProcessor implements BeanPostProcessor {public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException {return bean;} public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException {if (bean instanceof BeanSelfAware) {/ / if Bean implements the BeanSelfAware identity interface, inject the proxy object into ((BeanSelfAware) bean) .setSelf (bean) / / even prototype Bean can use this way} return bean;}}
According to whether the target object implements the BeanSelfAware identification interface, postProcessAfterInitialization injects the proxy object (bean) into the target object through setSelf (bean), so that it can complete the self-invocation within the target object.
Please refer to my post about the execution process of BeanPostProcessor, otherwise you can't continue to execute it.
IV. The problem of InjectBeanSelfProcessor
* * (1) scenario: * * when injecting proxy objects through InjectBeanSelfProcessor and circular dependency scenarios, the former cannot set proxy objects through setSelf. Circular dependency should be avoided, but in practice, it is inevitable that someone will use this kind of injection, after all, it is not mandatory.
(2) use cases
(2.1. define the identification interface that BeanPostProcessor needs to use.
As in 3.1, it will not be repeated here.
(2.2, Bean implementation
Java code
@ Service public class AServiceImpl implements AService, BeanSelfAware {/ / omit the Aservice interface definition here @ Autowired private BService bService; / / ① inject BService private AService self; / / ② into your own AOP proxy object public void setSelf (Object proxyBean) {this.self = (AService) proxyBean by @ Autowired / / ③ injects its own (target object) AOP proxy object System.out.println ("AService==" + AopUtils.isAopProxy (this.self)) through InjectBeanSelfProcessor; / / if the output true identifies AOP proxy object injection successfully} @ Transactional (propagation = Propagation.REQUIRED) public void a () {self.b () } @ Transactional (propagation = Propagation.REQUIRES_NEW) public void b () {}}
Java code
@ Service public class BServiceImpl implements BService, BeanSelfAware {/ / omit the Aservice interface definition @ Autowired private AService aService; / / ① injects AService private BService self; / / ② into its own AOP proxy object public void setSelf (Object proxyBean) {/ / ③ injects its (target object) AOP proxy object this.self = (BService) proxyBean via InjectBeanSelfProcessor System.out.println ("BService=" + AopUtils.isAopProxy (this.self)); / / if the output true identifies AOP proxy object injection successfully} @ Transactional (propagation = Propagation.REQUIRED) public void a () {self.b ();} @ Transactional (propagation = Propagation.REQUIRES_NEW) public void b () {}}
Here, A depends on B, B depends on A, that is, it constitutes circular dependency. the design problem of circular dependency is not discussed here (in practice, circular dependency should be avoided), but why it fails to inject proxy objects is discussed.
Please refer to my blog http://jinnianshilongnian.iteye.com/blog/1415278 for circular dependency.
For the order of initialization and destruction of dependencies, please refer to my blog http://jinnianshilongnian.iteye.com/blog/1415461.
(2. 3. InjectBeanSelfProcessor implementation
As in the previous 3.3, it will not be repeated here.
(2.4, test cases
Java code
RunWith (value = SpringJUnit4ClassRunner.class) @ ContextConfiguration (value = {"classpath:spring-config.xml"}) public class SelfInjectTest {@ Autowired AService aService; @ Autowired BService bService; @ Test public void test () {}}
Executing the test case as above will output:
BService=true
AService==false
That is, BService successfully injected the proxy object through InjectBeanSelfProcessor, but AService failed (actually injected the target object). Here is the information obtained by debug:
(2. 5. Why is this, and why does this happen in circular dependency?
Please look forward to my next analysis post.
3.4.The solution of improved version of InjectBeanSelfProcessor
Java code
@ Component public class InjectBeanSelfProcessor2 implements BeanPostProcessor, ApplicationContextAware {private ApplicationContext context; / / ① injection ApplicationContext public void setApplicationContext (ApplicationContext applicationContext) throws BeansException {this.context = applicationContext;} public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException {if (! (bean instanceof BeanSelfAware)) {/ / ② if Bean does not implement BeanSelfAware identity interface skip return bean } if (AopUtils.isAopProxy (bean)) {/ / ③ if the current object is an AOP proxy object, directly inject ((BeanSelfAware) bean) .setSelf (bean) } else {/ / ④ if the current object is not an AOP proxy, then get the proxy object through context.getBean (beanName) and inject / / this way is not suitable to solve the prototype Bean proxy object injection ((BeanSelfAware) bean) .setSelf (context.getBean (beanName));} return bean } public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException {return bean;}} 5, summary
Throughout it: [3.1 exposing Aop proxy objects through ThreadLocal] is suitable for solving the AOP proxy acquisition problem in all scenarios (whether singleton Bean or prototype Bean) (that is, it can solve the problem of self-invocation of the target object)
[3.2injecting proxy object into target object through initialization method] and [solution of InjectBeanSelfProcessor improved version 3.4] can solve the problem of ordinary (no cyclic dependency) AOP proxy object injection, and can also solve the problem that the target object cannot be injected into AOP proxy object caused by circular dependency (which should be the circular dependency between singleton) mentioned in [3.3i]. However, this solution is not suitable for solving the problem of self-invocation of prototype Bean in circular dependencies.
[3. 3 injecting proxy objects into the target object through BeanPostProcessor]: can only solve the ordinary (no circular dependency) Bean injection AOP proxy, can not solve the circular dependency AOP proxy object injection problem, that is, can not solve the target object self-invocation problem.
Jingnianshilongnian wrote
Circular dependencies allowed by spring (only singletons and prototype Bean are considered): Amurmuri B and B are allowed only if An and B are not prototypes, that is, an error will be reported if both An and B are prototype (circular dependencies of prototype Bean cannot be performed). A (singleton)-B (singleton) or A (prototype)-B (singleton) this is OK, but this is not allowed when A (prototype)-B (prototype) or A (prototype)-B (singleton Lazy) [and context.getBean ("A")].
A (prototype)-- B (prototype) A (prototype)-- B (single case Lazy) [and context.getBean ("A")] will report: Error creating bean with name 'BServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.sishuok.issue.AService com.sishuok.issue.impl.BServiceImpl.aService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name' AServiceImpl': Injection of autowired dependencies failed Nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.sishuok.issue.BService com.sishuok.issue.impl.AServiceImpl.bService; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'BServiceImpl': Requested bean is currently in creation: [color=red] Is there an unresolvable circular referral [/ color]?
Second, A (prototype)-B (singleton) and A (singleton)-B (singleton) this way to use my [3. 3 injection proxy object into the target object through BeanPostProcessor] is no problem.
Therefore, [3. 4 improved version of InjectBeanSelfProcessor's solution] can be used as the final solution.
After reading the above, do you know how to solve the problem that the Spring service method call another method transaction does not take effect in this class? 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.