How to do self injection Spring Boot 3+, in order to pass through proxy and apply transactional behavior

102 Views Asked by At

I'd like to create a self reference in my service so that I can use it to go through Spring's proxy and apply transactional behavior.

@RequiredArgsConstructor
public class Service {
  @Lazy private final Service self;

  public void foo() {
    self.bar();
  }

  @Transactional
  public void bar() {
    // do some transactional stuff
  }
}

I want to do that also to be compliant with java:S6809

A method annotated with Spring’s @Async or @Transactional annotations will not work as expected if invoked directly from within its class.
This is because Spring generates a proxy class with wrapper code to manage the method’s asynchronicity (@Async) or to handle the transaction (@Transactional). However, when called using this, the proxy instance is bypassed, and the method is invoked directly without the required wrapper code.

The issue is that I cannot do it otherwise I get an error on startup

The dependencies of some of the beans in the application context form a cycle:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
  • I used @Lazy
  • I used lombok.copyableAnnotations += org.springframework.context.annotation.Lazy in lombok.config

Is setting spring.main.allow-circular-references to true the only solution to do self-injection in Spring?

(some references: https://medium.com/javarevisited/spring-transactional-mistakes-everyone-did-31418e5a6d6b)

1

There are 1 best solutions below

0
emptak On BEST ANSWER

You can try TransactionTemplate instead of @Transactional

@RequiredArgsConstructor
class MyService {
    @Lazy
    private final MyService self;
    private final TransactionTemplate transactionTemplate;


    public void foo() {
        self.bar();
    }

    public void bar() {
        transactionTemplate.executeWithoutResult(ts -> {
            // do tx stuff
        });
    }
}

see https://docs.spring.io/spring-framework/reference/data-access/transaction/programmatic.html