Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Null Pointer Exception In Spring Proxy Class and Kotlin

I am facing some problems with kotlin in conjunction with spring.

I have a controller bean (without an interface btw) which has an auto-wired service bean via the primary constructor.

It works perfectly unless I use caching annotations for the controller. Apparently springs caching generates a proxy class under the hood which deals with the caching.

My code looks like this:

@RestController
@RequestMapping("/regions/")
open class RegionController @Autowired constructor(val service: RegionService) {
    @RequestMapping("{id}", method = arrayOf(RequestMethod.GET))
    @Cacheable(cacheNames = arrayOf("regions"))
    fun get(@PathVariable id: Long): RegionResource {
        return this.service.get(id)
    }
}

The problem now is a null pointer exception when the method is executed, actually this.service is null which technically is not possible as it is a nonnull variable in kotlin.

I assume that class proxies generated by spring initialize the class with null values instead of the autowired bean. This must be a common pitfall using kotlin and spring. How did you circumvent this problem?

like image 526
linqu Avatar asked May 25 '16 08:05

linqu


2 Answers

In Kotlin both classes and members are final by default.

For the proxying library (CGLIB, javaassist) to be able to proxy a method it has to be declared non final and in a non final class (since those libraries implement proxying by subclassing). Change your controller method to:

@RequestMapping("{id}", method = arrayOf(RequestMethod.GET))
@Cacheable(cacheNames = arrayOf("regions"))
open fun get(@PathVariable id: Long): RegionResource {
    return this.service.get(id)
}

You probably see a warning in console regarding RegionController methods not being subject to proxying.

The Kotlin compiler plugin

The Kotlin team has acknowledged this difficulty and created a plugin that marks the standard AOP proxy candidates e.g. @Component with open.

You can enable the plugin by in your build.gradle:

plugins {
  id "org.jetbrains.kotlin.plugin.spring" version "1.1.60"
}
like image 178
miensol Avatar answered Nov 07 '22 23:11

miensol


Soon this might not be a problem any longer.

There is work in progress that any lib (including spring for example) can specify a list of annotations a file in META-INF. Once a class is annotated with one of these, it will default to open for the class itself and all its functions. This is also true for classes inheriting from an annotated class.

For more details, have a look at https://github.com/Kotlin/KEEP/pull/40#issuecomment-250773204

like image 35
DaImmi Avatar answered Nov 08 '22 00:11

DaImmi