Springboot AOP with java && kotlin and Gradle - it's not working

44 Views Asked by At

I am a student working on a Java + Spring Boot project, and I changed the Controller part to Kotlin! However, when I tested it, all APIs that use @Before Aop generate NPE when calling the service from the controller, and the service is null! Java Controller -> AOP is working normally, but I don't know why.

Below is the code

Controller

@RestController
@RequestMapping("/")
open class SampleController (
    private val sampleService: SampleService){

   @GetMapping
   fun test(){
   println("TEST In ")
  }
}

AOP

@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class PreProcess{
   @Before("@within(org.springframework.web.bind.annotation.RestController)")
public void doProcess(JoinPoint joinPoint) {
   log.info("####### ASPECT IN ")
}
}

to summarize If you burn the Java Controller API, both aspect and controller logs are recorded and operate normally.

If you burn the Kotlin controller API, an NPE will occur (Cannot invoke SampleService because this.SampleService is null ~~~~)

What is the solution?,,,,,,,

I tested it by creating a new project, and also deleted gradle that seemed related.

1

There are 1 best solutions below

0
Malvin Lok On

Your issue seems to arise from a problem with the initialization order of your components. When the @Before aspect attempts to access sampleService, its initialization may not yet be completed, resulting in a NullPointerException (NPE) as it uses sampleService.

To ensure that sampleService would initialize prior to it being accessed, you can convert sampleService to a lateinit var and autowire it using field injection. Here is how your Kotlin controller might look:

@RestController
@RequestMapping("/")
open class SampleController : SampleService{

    @Autowired 
    lateinit var sampleService: SampleService

   @GetMapping
   fun test() {
        println("TEST In ")
    }
}

Keep in mind that using lateinit var is not always the best option due to potentially unclear initialization semantics.

Another potential issue might be related to incompatibility of Lombok's @RequiredArgsConstructor annotation while using it with Kotlin, as Kotlin doesn't generate zero-argument constructors which this annotation needs.

A better alternative would be for you to construct your own constructor for dependency injection in your Aspect.

@Aspect
@Component
@Slf4j
class PreProcess(
    private val sampleService: SampleService) {
    @Before("@within(org.springframework.web.bind.annotation.RestController)")
    fun doProcess(joinPoint: JoinPoint) {
        log.info("####### ASPECT IN ")
    }
}

Although, in general, it is not a good idea to mix up service components with aspects, because these are meant for different concerns.