Search code examples
springspring-bootdependency-injectionproxyaop

@Controller, @Service, @Repository beans fails on @EnableTransactionManagement in mode = AdviceMode.PROXY or default fails


Why using @EnableTransactionManagement in default mode or PROXY mode attempts against beans creation like @Controller, @Service, and @Repositories. I don't discard another kind of beans targeting this bad behavior.

I have a project with:

  • SpringBoot 2.1.8.RELEASE + JPA + Rest Controller
  • PostgreSQL 9.6
  • @Transactional annotation on methods like save, update and delete (also, batch delete)

It creates proxy classes and nulls the remaining beans in the ApplicationContext.

EnableTransactionManagement default/PROXY advice mode


Solution

  • The default mode is PROXY. When ASPECTJ advice mode is set, all the beans are injected in the right place. Check Spring current docs on the subject.

    The mode() attribute controls how advice is applied: If the mode is AdviceMode.PROXY (the default), then the other attributes control the behavior of the proxying. Please note that proxy mode allows for interception of calls through the proxy only; local calls within the same class cannot get intercepted that way.

    Note that if the mode() is set to AdviceMode.ASPECTJ, then the value of the proxyTargetClass() attribute will be ignored. Note also that in this case the spring-aspects module JAR must be present on the classpath, with compile-time weaving or load-time weaving applying the aspect to the affected classes. There is no proxy involved in such a scenario; local calls will be intercepted as well.

    Like this:

    EnableTransactionManagement ASPECTJ advice mode

    Still, @Transactional methods fail. Due to "TransactionRequiredException", so ASPECTJ does not solve the problem in persistence layer, only grants beans injection (maybe no platform transaction manager is created). What to do next?

    See the transaction exception:

    Transaction Exception

    !!! Solution:

    When working with transactions, the scope has to be shared in the beans chain: @Service(also, caller method)<-@Repository(also, transactional method), @Service context (class, method) shall be marked as @Transactional. Applies to annotated methods or classes in the call stack that ends in transactional operation (bottom-up approach).

    Annotation sequence:

    1. @EnableTransactionManagement (in @Configuration alike classes or @SpringBootApplication -autoconfiguration-)
    2. @Transactional annotated method/classes in beans injection chain (bottom-up: persistent method/class, then @Service/@Component layers)
    3. @Autowired annotation on beans candidates (Service instance in @Controller class, Repository instance in @Service class)

    Note: Spring AOP does not create proxy properties from functional interfaces based features, only object instances so recommended is using methods encapsulating persistence logic.

    e.g

    Function<T, R> function 
    BiFunction<T1,T2,R> function 
    Supplier<T> supplier
    ...