Spring boot 2 @Transactional annotation makes Autowired fields null
Transaction, amongst others, are applied using AOP, the default AOP mechanism in Spring is to use proxies. When using Spring Boot the proxy mode is set the class based proxies.
You can fix this in 1 of 2 ways.
- Remove
final
from your method - Disable class based proxies by adding
spring.aop.proxy-target-class=false
to yourapplication.properties
Now when you aded @Transactional
this will lead to a proxy of your UserServiceImpl
to be created, a class-based proxy to be exact. What happens is that a subclass is created for your UserServiceImpl
and all methods are overriden to apply the TransactionInterceptor
. However as your method is marked final
the dynamically created class cannot override this method. As a result the method looks at field instances in the dynamically created proxy class which always will be null
.
When removing final
the method can be overridden, the behavior applied and it will look at the proper field instances (of the actual UserServiceImpl
instead of the proxy).
When disabling class based proxies, you will get a JDK Dynamic Proxy which is basically a thin wrapper which implements all the interfaces your service implements. It applies the added behavior (transactions) and calls the actual service. There is no extension of the actual class needed and as such you can proxy final methods (as long as it is part of your interface).
I faced the same problem working with Kotlin. When I added the @Transactional
annotation to a method inside a service, I got a message saying Methods annotated with '@Transactional' must be overridable
so I went ahead and marked both, the class and the method as open
. Easy, right?! Well, not quite.
Although this compiles, I got the required repository as null at execution time. I was able to solve the problem in two ways:
- Mark the class and ALL of its methods as
open
:
open class FooService(private val barRepository: BarRepository) {
open fun aMethod(): Bar {
...
}
@Transactional
open fun aTransactionalMethod(): Bar {
...
}
}
This works but having all the methods in a class marked with open
might look a bit odd, so I tried something else.
- Declare an interface:
interface IFooService {
fun aMethod(): Bar
fun aTransactionalMethod(): Bar
}
open class FooService(private val barRepository: BarRepository) : IFooService {
override fun aMethod(): Bar {
...
}
@Transactional
override fun aTransactionalMethod(): Bar {
...
}
}
This way you can still use the annotation since all the methods will be overridable and you won't need to use open
everywhere.
Hope this helps =)