1

I am autowiring service in controller. And in service, I have a scenario where I need to throw an exception and DB changes also. So, I tried @Async

@Transactional
public void verifyOtp(OtpDto otpDto)
   ...
   if(xyz){
       deactivateOtp(emp.getId());
       throw new ServException("Mobile no requested is already assigned", "error-code");
   }
}

@Async
@Transactional //with or without
public void deactivateOtp(Integer id){
     otpRepo.deactivateOtp(id);
}

public interface OtpRepository extends JpaRepository<Otp, Integer> {
    @Modifying
    @Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(@Param("id") Integer id);

This is not creating new thread. But, if I gives at repo, it works

public void deactivateOtp(Integer id){
     otpRepo.deactivateOtp(id);
}

public interface OtpRepository extends JpaRepository<Otp, Integer> {
    @Async
    @Transactional
    @Modifying
    @Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(@Param("id") Integer id);
Satish Patro
  • 3,645
  • 2
  • 27
  • 53
  • Does this answer your question? [Spring AOP not working for method call inside another method](https://stackoverflow.com/questions/13564627/spring-aop-not-working-for-method-call-inside-another-method) – Alan Hay Dec 10 '19 at 10:56

2 Answers2

1

This is discussed many times.

Basically, Spring AOP will not intercept local method call (in your case call deactivateOtp() within the same class)

You can read more about this behavior here: Understanding AOP proxies

Highlight:

The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.

Mạnh Quyết Nguyễn
  • 17,677
  • 1
  • 23
  • 51
1

First of all check that the service is wrapped into proxy (you can place a breakpoint in controller and see the reference to the service, it will be with proxy). Otherwise there is something wrong with the configuration and @Transactional/@Async won't work till you fix that.

Now, assuming this is not an issue, there is an issue in the code:

When the controller calls service.verifyOtp it goes to the proxy (to handle the transaction) and then to your implementation.

But when it reaches your implementation and you call the method that belongs to the same impl, it doesn't pass through the proxy again, instead it directly goes to the deactivateOtp as if there is no spring at all here. Of course, @Async doesn't work.

In terms of resolution:

  1. Consider using self injection if you work with spring 4.3+. Read this thread for more information.

  2. Alternatively, refactor your code so that the deactivateOtp will be a public method of another class. In this case the call won't be "internal" anymore, it will path through Proxy hence the @Async will work.

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • I created another class and kept verifyOtp as public method and annotated with @async. In serviceimpl class, I autowired new class and called it as per your 2nd approach. But, it still using the same thread, not a new thread – Satish Patro Dec 17 '19 at 03:49