为啥我可以在没有请求的情况下注入请求范围的 bean?
Posted
技术标签:
【中文标题】为啥我可以在没有请求的情况下注入请求范围的 bean?【英文标题】: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.
应该如此。
(代码示例见文末)
我知道三种方法可以通过以下测试变绿:
-
更改
UsingBean
的范围
方法注入
范围代理
([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 谢谢。想从中找出答案吗?
【参考方案1】:
尽管您可能认为您没有当前的请求和会话,但实际上您确实有。
@WebAppConfiguration
是触发它的原因。它将激活ServletTestExecutionListener
,它将注册一个线程绑定的模拟请求和响应。
这解释了为什么测试成功,因为存在线程绑定请求。
【讨论】:
以上是关于为啥我可以在没有请求的情况下注入请求范围的 bean?的主要内容,如果未能解决你的问题,请参考以下文章
将较短范围的 Bean 实例注入 CDI 中较大范围的 bean 实例 - 它是如何工作的?
Spring:如何将 HttpServletRequest 注入到请求范围的 bean 中?