Spring Boot @Autowired with Kotlin in @Service is

2019-04-06 04:04发布

问题:

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?

回答1:

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)
   }

} 


回答2:

Which Spring Boot version do you use? Since 1.4 Spring Boot is based on Spring Framework 4.3 and since then you should be able to use constructor injection without any @Autowired annotation at all. Have you tried that?

It would look like this and works for me:

@Service
class UserServiceController(val dsl: DSLContext, val teamService: TeamService) {

  // your class members

}