Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot @Autowired with Kotlin in @Service is always null

Tags:

Currently I try to rewrite my Java Spring Boot Application with Kotlin. I encountered a problem that in all of my classes which are annotated with @Service the dependency injection is not working correctly (all instances are null). Here is an example:

@Service @Transactional open class UserServiceController @Autowired constructor(val dsl: DSLContext, val teamService: TeamService) {   //dsl and teamService are null in all methods } 

Doing the same in Java works without any problems:

@Service @Transactional public class UserServiceController {     private DSLContext dsl;     private TeamService teamService;      @Autowired     public UserServiceController(DSLContext dsl,                              TeamService teamService)     {         this.dsl = dsl;         this.teamService = teamService;     } 

If I annotate the component with @Component in Kotlin everything works fine:

@Component open class UserServiceController @Autowired constructor(val dsl: DSLContext, val teamService: TeamService) {   //dsl and teamService are injected properly } 

Google provided many different approaches for Kotlin and @Autowired which I tried but all resulted in the same NullPointerException I would like to know what the difference between Kotlin and Java is and how I can fix this?

like image 610
Deutro Avatar asked Dec 23 '16 09:12

Deutro


People also ask

Why is my Autowired field null?

The field annotated @Autowired is null because Spring doesn't know about the copy of MileageFeeCalculator that you created with new and didn't know to autowire it.

Why is @autowired not working?

When @Autowired doesn't work. There are several reasons @Autowired might not work. When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection.

What is @autowired annotation in Spring boot?

The @Autowired annotation provides more fine-grained control over where and how autowiring should be accomplished. The @Autowired annotation can be used to autowire bean on the setter method just like @Required annotation, constructor, a property or methods with arbitrary names and/or multiple arguments.

What is Autowired in Kotlin?

Kotlin @Autowired constructor injection This annotation instructs the Spring framework to inject the owner dependency into the Car bean.


2 Answers

I just bumped into exactly same issue - injection worked well, but after adding @Transactional annotation all the autowired fields are null.

My code:

@Service @Transactional   open class MyDAO(val jdbcTemplate: JdbcTemplate) {     fun update(sql: String): Int {        return jdbcTemplate.update(sql)    }  }  

The problem here is that the methods are final by default in Kotlin, so Spring is unable to create proxy for the class:

 o.s.aop.framework.CglibAopProxy: Unable to proxy method [public final int org.mycompany.MyDAO.update(... 

"Opening" the method fixes the issue:

Fixed code:

@Service @Transactional   open class MyDAO(val jdbcTemplate: JdbcTemplate) {     open fun update(sql: String): Int {        return jdbcTemplate.update(sql)    }  }  
like image 58
miran Avatar answered Sep 28 '22 00:09

miran


I faced the same problem working with Kotlin but the null instance was a JpaRepository. 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:

  1. 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.

  1. 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 =)

like image 31
Jhoan Manuel Muñoz Serrano Avatar answered Sep 27 '22 23:09

Jhoan Manuel Muñoz Serrano