Spring Boot - A bean referencing itself to trigger AOP #Async within the same class, not working - spring

I want to within my own Bean call a method within itself which has its own #Async and #Transactional proxies but it isnt working.
public class MyClass {
private MyClass _selfWithProxy;
#PostConstruct
public void postContruct() {
_selfWithProxy = applicationContext.getBean(MyClass.class);
}
void myMethodA() {
_selfWithProxy.myMethodB()
}
#Async
#Transactinal
void myMethodB() {
//do stuff
}
However when I call myMethodA from another Bean the call to myMethodB does not intercept with an Async interceptor. I can see _selfWithProxy is a proxy bean, and the proxy activates but there are is no Async interceptor in the chain.
When I call myMethodB from another bean the #Async works, so I know it is setup correctly

Self invocation won't work for #Async. I believe that is because the proxy (your code + AOP advice) object calls your methodA on the target object (just your code) which then calls it's own methodB which isn't advised by AOP. In general I would not advise having spring bean methods call other methods of the same bean.
"#Async has two limitations:
It must be applied to public methods only.
Self-invocation — calling the async method from within the same class — won't work.
The reasons are simple: The method needs to be public so that it can be proxied. And self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly."
https://www.baeldung.com/spring-async

Related

AfterReturning advice not executed [duplicate]

This question already has answers here:
Why does self-invocation not work for Spring proxies (e.g. with AOP)?
(2 answers)
Closed 4 months ago.
#Component
Class A {
public String methodA() {
return methodB();
}
#Publish
public String methodB() {
return "abc";
}
}
#Component
#Aspect
public class PublishEvents {
#AfterReturning(value = "#annotation(Publish)", returning = "retVal")
public void sendEvent(JoinPoint joinPoint, Object retVal) {
system.out.println("sending event");
}
}
So I have an aspect class PublishEvents defined with AfterReturning Advice. This advice gets executed when I use #Publish which is a custom defined annotation, over methodA of class A but it does not get executed when I put it over methodB of class A. Note that methodB is called from methodA.
Can someone tell me what I am missing here?
Spring AOP is proxy-based, which means that an additional class for proxying bean calls is created by Spring, for more details about proxy mechanisms check the link.
The key thing to understand is that calls on that component reference are calls on the proxy. To make it more clear, other beans, that autowire the A component, actually contain the proxied instance, not the component instance itself. As a result, the proxy can delegate to all the interceptors (advice) relevant to that particular method call.
However, any method calls it may make on itself, such as methodA invoking methodB in your case, will be invoked against the instance itself, not the proxy. This has important implications. It means that self-invocation will not result in the AfterReturning advice getting a chance to run.
Unlike Spring AOP, AspectJ has no issues with self-invocation since it's not a proxy-based AOP framework. Check Indra's answer for more details and examples.
For reference: Understanding AOP Proxies

How to handle Spring transactions inside coroutines?

It is known that Spring transactions are tied to threads: there are thread locals specific for an ongoing transaction. And transactions know nothing about coroutine context. So what if I would like to call a #Transactional method from inside a coroutine: is it safe?
Imagine we have a method inside JobProcessor bean processing a job list. Each job is processed inside async{}. And I would like to update DB after every successful or failed processing using #Transactional methods of bean JobService.
class JobProcessor {
fun process(jobs: List<MyJob>) =
jobs.map { job ->
async {
try {
....//processing
jobService.success(job)
} catch (t: Throwable) {
jobService.failure(job)
}
}
}
class JobService {
#Transactional
fun success(job: MyJob) {...}
#Transactional
fun failure(job: MyJob) {...}
}
First, please keep in mind that annotating bean methods with #Transactional will not suffice - make sure you enabled declarative transaction handling, e.g. by adding #EnableTransactionManagement to a #Configuration class or by using <tx-annotation-driven /> in XML config.
Regarding your question: There will only be a transactional context while executing Spring bean methods annotated with #Transactional when they are being invoked from a Spring bean outside of their containing class! Declarative transactions in Spring rely on AOP proxy classes being created for the #Transactional annotated classes by Spring. At runtime if a Spring bean A calls a #Transactional method on a Spring bean B, the call will be intercepted by an AOP proxy that transparently spawns a transaction, calls the original method of Spring bean B and commits or rollbacks this transaction afterwards.
Remember: Only external method calls that come in through the proxy will be intercepted – any self-invocation calls, as in your example this.process() calling #Transactional methods this.success() or this.failure() will not start any transaction – even if the method is annotated with #Transactional.

Whether and When Spring Lock Table if Method Annotated with Transactional

This question comes from that, if I have a methodA, and methodB in classA like below
public class classA {
public void methodA() {
// some code
...
// time to deal with db
methodB();
}
#Transactional
public void methodB() {
// insert a record
throw new RuntimeException("testing");
}
}
In classB Directly call methodA, there will be no transactional effect on methodB(this is to say spring will not rollback the insert operation even RuntimeException occurred).
When I move the #Transactional from methodB to methodA, and call methodA again, the #Transactional annotation works.
However, if I have really a lot of work to do in methodA, will spring lock the table during the total execution time?
Well, I will add a new class(i.e. classC) and move methodB(annotated with Transactional) to the new class as workaround.
It is the limitation of Springs AOP that #Transactional will not have effect if the method is called within the same class.
Refer to #Transactional method called from another method doesn't obtain a transaction
Spring doesn't handle transaction by itself. #Transactional annotation lets Spring's proxy class to inject transaction begin and commit/rollback methods around your original method.
Proxy wrapper will only be called if you invoke annotated method from outside the classes. Local method call will not use proxy class. So, call from method A to method B will not use transactional features.
Secondly, Spring will simply delegate the transaction handling to underlying database driver. Whether your table acquires a lock or not will be determined by your driver documentation and (atomicity and isolation level) settings.

Spring transaction when calling private method

I have two questions.
If I have a method:
#Transactional
public method1(){
method2()
}
public method2(){
dao.save()
}
If there is an exception in method2(), will there be a rollback?
Another question:
If I have a method:
#Transactional
public method1(){
method2()
}
private void method2(){
dao.save()
}
If there is an exception in method2(), will there be a rollback?
Yes, there will be a rollback.
The private methods will run within the same transaction. You should be aware that you can't have a #Transactional private method. It will not work without raising any error. This behavior is explained in Spring Docs:
Due to the proxy-based nature of Spring’s AOP framework, calls within
the target object are by definition not intercepted. For JDK proxies,
only public interface method calls on the proxy can be intercepted.
Yes to both. Transactional method means there must be no error during the entire runtime of the method.
If there is an error on one of the methods you are calling from within, these errors will be propagated and make the transaction fail and rollback.

How can I make sure a "pool" bean gets all the other beans it needs?

I have a spring config where I define hundreds of actions which extend MyAction. I have a pool where an execution service can look up actions. I can't use the appContext directly because each action has one or more "keys" which the execution service will use and pool cuts that dependency.
So the pool must be able to collect all beans of type MyAction (or rather beans that extend MyAction).
The method ApplicationContext.getBeansOfType() seems to do what I need but when can I safely call it?
It would be great if I could call it in a #PostConstruct method but is it guaranteed that the bean factory has seen added each and every bean from the config at that time?
Note: Almost all of my beans are #Lazy
You could use injection by constructor and passing a Collection of yours MyAction
Something like
#Component
public class Foo {
private final Set<MyAction> myActions;
#Inject
public Foo(Set<MyAction> myActions) { this.myActions = myActions; }
}
or
public class Foo {
private Set<MyAction> myActions;
#Inject
public void setMyActions(Set<MyAction> myActions) { this.myActions = myActions; }
}
Spring will take care of creating the set with all beans that extends MyAction.
In the first case, they are injected by constructor, you can safely use them in any method.
In the second case, Spring will eventually call the setter. You can either do any post processing in the setter or add a #PostConstruct method that works on myActions.
Try to use ListableBeanFactory like this. Also here is API documentation. Method getBeansOfType has parameter allowEagerInit which forces eager init of lazy object.

Resources