【问题标题】:Session-scoped bean has dependency set to null when written in Kotlin使用 Kotlin 编写时,会话范围的 bean 的依赖项设置为 null
【发布时间】:2020-10-13 11:42:52
【问题描述】:

使用 spring boot 2.3.4、kotlin 1.3.72。

我有一个会话范围的 bean,Spring 正在用空依赖项实例化它,这显然会在执行时导致 NPE。我创建了一个示例项目来重现该行为。 等效代码在 Java 中有效。 更新:如果我只将 MessageWriterService 转换为 Java,则以下内容有效。

这是我的配置类:

@SpringBootApplication
class SessionScopeKotlinApplication {

    @Bean
    fun messageWriter() = MessageWriter()

    @Bean
    @SessionScope
    fun messageWriterSvc(writer: MessageWriter) = MessageWriterService(writer)

}

MessageWriter 和 MessageWriterService

class MessageWriter {
    fun write() = println("message written!")
}

open class MessageWriterService(private val writer: MessageWriter) {

    private var written = false

    fun writeIfNotWritten() {
        if (!written) {
            writer.write()
            written = true
        } else {
            println("** message already written, not writing again **")
        }
    }
}

然后我有一个调用 MessageWriterService 的控制器:

@RestController
class MessageWriterController(private val writerService: MessageWriterService) {
    @GetMapping("/write")
    fun write() {
        writerService.writeIfNotWritten()
    }
}

只要我调用控制器方法,我就会得到下面的堆栈跟踪(缩写)。失败点是对 writer 的调用,实际上是 null。

2020-10-13 07:37:41.919 ERROR 9184 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
    at com.example.sessionscopekotlin.MessageWriterService.writeIfNotWritten(SessionScopeKotlinApplication.kt:32) ~[main/:na]
    at com.example.sessionscopekotlin.MessageWriterController.write(SessionScopeKotlinApplication.kt:44) ~[main/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]

【问题讨论】:

  • 如果您突出显示出现 NPE 的行会有所帮助。

标签: spring spring-boot kotlin


【解决方案1】:

在我看来,Spring 似乎无法在此处为您自动装配 Writer

@SpringBootApplication
class SessionScopeKotlinApplication {

    @Bean
    fun messageWriter() = MessageWriter()

    @Bean
    @SessionScope
    fun messageWriterSvc(writer: MessageWriter) = MessageWriterService(writer)

}

解决办法是这样调用bean函数:

@SpringBootApplication
class SessionScopeKotlinApplication {

    @Bean
    fun messageWriter() = MessageWriter()

    @Bean
    @SessionScope
    fun messageWriterSvc() = MessageWriterService(messageWriter())

}

如果您不希望每次调用都返回相同的 Writer,您还应该使用 prototype 范围。

【讨论】:

  • 果然,解决了。我在 Java 中进行了相同风格的自动装配,并且成功了。奇怪的是它在 Kotlin 中没有。无论如何,谢谢!
  • 我认为也可以使用@Autowired注解。
【解决方案2】:

有趣的发现。如果您不想为每个会话创建新的 MessageWriter,以下方法可能会有所帮助:

@SpringBootApplication
class SessionScopeKotlinApplication {

    @Bean
    fun messageWriter() = MessageWriter()

}

@Service
@SessionScope
class MessageWriterService(private val writer: MessageWriter) {

    private var written = false

    fun writeIfNotWritten() {
        if (!written) {
            writer.write()
            written = true
        } else {
            println("** message already written, not writing again **")
        }
    }
}

所以你基本上从@Bean 注释切换到直接@Service 声明。但我可以想象在某些情况下,您想显式使用 @Bean 注释。

【讨论】:

    猜你喜欢
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 2011-04-16
    • 2017-04-25
    • 2016-05-28
    • 1970-01-01
    • 2018-04-24
    • 2012-05-26
    相关资源
    最近更新 更多