在下面的演示中,我们创建了一个新的ReactiveSecurityContextHolder
,以不同的用户身份运行一个操作(在我们的实际代码中,我们创建身份验证的方式有点复杂,但对于演示,下面显示了我们遇到的相同问题)。
@Service
final class SomeServiceImpl(
private val repo: SomeRepository,
) : SomeService {
@Transactional
suspend fun foo() {
val a = repo.save(Some())
// expected: 1
println(repo.count()); // actual: 1
withContext(
ReactorContext(
ReactiveSecurityContextHolder.withAuthentication(
RunAsUserToken(
it.userId.toString(),
it.userId,
it.userId,
mutableListOf(),
AbstractAuthenticationToken::class.java,
),
),
)
) {
// expected: 1
println(repo.count()); // actual: 0
}
}
}
为什么withContext
块中的repo.count()
不返回1而是返回0?
当使用withContext(Dispatchers.IO)
时,内层也返回1。
对我来说,在上下文更改期间,事务似乎丢失了,但我不知道如何保留它。
1条答案
按热度按时间vd2z7a6w1#
回答我自己的问题
TL;DR
这是:
...覆盖
coroutineContext
中的ReactorContext
键(注意小写的c
,coroutineContext
指的是当前协程中的CoroutineContext
)。键被withContext
覆盖,并且不合并。ReactorContext
键不会与现有的ReactorContext
合并,而是被覆盖。因此,保存当前事务的现有ReactorContext
被隐藏。更多解释和解决方案
重要的是要理解,在
coroutineContext
中有一个ReactorContext
,并创建一个新的ReactorContext
作为withContext
的参数。这两个ReactorContext
彼此独立存在,需要手动合并以获得单个ReactorContext
,然后可以合并到coroutineContext
(阅读:用合并不同ReactorContext
中的项的项来覆盖现有的项。下面是一个解决问题的实现:
对我来说,最难理解的部分是:
ReactorContext
既是类的名称,也是(在Kotlin中)类的名称引用它的同伴对象,它恰好是一个Key
,然后用于在CouroutineContext
中查找某些内容,更清楚地说,coroutineContext[ReactorContext]
实际上表现得像coroutineContext[ReactorContext.Key]
。1.每个
ReactorContext
都没有不同的密钥,ReactorContext
中的密钥也不会传播到CoroutineContext
。ReactorContext
只是 Package 了reactor上下文,并通过CoroutineContext
中的ReactorContext.Key
将其提供给协程。我假设ReactorContext
中的Map会自动合并到CoroutineContext
中,这是不正确的。