【问题标题】:Why can I inject request-scoped beans in the absence of a request?为什么我可以在没有请求的情况下注入请求范围的 bean?
【发布时间】:2019-09-12 10:48:25
【问题描述】:

如果我尝试将请求范围的 bean 注入到单例范围的 bean 中,则会失败,因为

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

应该如此。

(代码示例见文末)

我知道三种方法可以通过以下测试变绿:

  1. 更改UsingBean的范围
  2. 方法注入
  3. 范围代理

([1] 与其说是一种解决方案,不如说是一种 hack,并且可能会导致进一步的问题,但它确实将测试变为绿色。:P)

虽然我确实理解这三个选项背后的想法,但我完全不明白为什么它们会起作用。

我的意思是,即使我在 [1] 中将范围更改为“会话”,当我实例化 UsingBean 时,我仍然既没有会话也没有请求。

至于 [2] 和 [3],它们避免在启动时获取实例,但是当它们实际获取实例时我仍然没有请求。

但测试并没有失败。为什么?

代码示例

假设我有一个请求范围的 bean

@Repository
@Scope("request")
class RequestScopedBean : ScopedBean{
    override fun foo(): String {
        return "Hello World"
    }
}

在单例范围内使用

@Service
class UsingBean{
    private val scopedBean:ScopedBean

    @Inject
    constructor(scopedBean: ScopedBean) {
        this.scopedBean = scopedBean
    }

    fun foo():String{
        val foo = scopedBean.foo()
        println(foo)
        return foo
    }
}

我们也为此创建一个小测试:

@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest
@WebAppConfiguration
class RequestScopedBeansIT{

    @Inject
    private lateinit var bean : UsingBean

    @Test
    fun canInject(){
        assertThat(bean.foo()).isEqualTo("Hello World")
    }
}

1) 改变UsingBean的范围

@Service
@Scope("session")
class UsingBean{
    private val scopedBean:ScopedBean

    @Inject
    constructor(scopedBean: ScopedBean) {
        this.scopedBean = scopedBean
    }

    fun foo():String{
        val foo = scopedBean.foo()
        println(foo)
        return foo
    }
}

2) 方法注入

@Service
class UsingBean{
    private val scopedBean:ScopedBean
        get() = injectBean()

    fun foo():String{
        val foo = scopedBean.foo()
        println(foo)
        return foo
    }

    @Lookup
    fun injectBean():ScopedBean{
        TODO("will be replaced by spring")
    }
}

3) 范围代理

@Repository
@Scope("request",proxyMode = ScopedProxyMode.TARGET_CLASS)
class RequestScopedBean : ScopedBean{
    override fun foo(): String {
        return "Hello World"
    }
}

@Repository
@RequestScope
class RequestScopedBean : ScopedBean{
    override fun foo(): String {
        return "Hello World"
    }
}

【问题讨论】:

  • 您确实有一个请求,这就是 @WebAppConfiguration 正在处理的问题。它将MockServletRequest 等绑定到当前正在执行的线程。所以是的,你确实有一个请求。
  • @M.Deinum 谢谢。想从中找出答案吗?

标签: java spring spring-boot kotlin dependency-injection


【解决方案1】:

尽管您可能认为您没有当前的请求和会话,但实际上您确实有。

@WebAppConfiguration 是触发它的原因。它将激活ServletTestExecutionListener,它将注册一个线程绑定的模拟请求和响应。

这解释了为什么测试成功,因为存在线程绑定请求。

【讨论】:

    猜你喜欢
    • 2016-04-29
    • 1970-01-01
    • 2015-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-26
    • 2020-11-10
    • 1970-01-01
    相关资源
    最近更新 更多