为什么在没有请求的情况下注入请求范围的bean?

时间:2019-04-23 09:20:36

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

如果我尝试将请求范围的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. 方法注入
  4. 作用域代理

([1]不仅仅是解决方案,而且可能会进一步导致问题,但是确实使测试变成绿色。:P)

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

我的意思是,即使我在[1]中将范围更改为“ session”,当实例化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"
    }
}

1 个答案:

答案 0 :(得分:1)

尽管您可能会认为您没有当前请求,但实际上您确实拥有一个。

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

其中解释了为什么存在线程绑定请求而测试成功的原因。