并行上下文中的@WithMockUser
@WithMockUser in a parallel context
下面的代码只是我想要实现的一个简化版本。
我正在使用 @WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"})
在我的测试中模拟用户。
@RunWith(SpringRunner.class)
@SpringBootTest
@EnableAsync
@WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"})
public class NonTest {
@Test
public void test() {
IntStream.range(1, 10)
.parallel() // Comment this and it will work
.forEach(value -> {
getUser(value);
});
}
public void getUser(int value) {
System.out.println(value + ": " + ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
}
}
当我执行上面的代码并注释掉 .parallel()
时,它可以正常工作并按预期打印出以下内容:
1: jane@no-domain.com
2: jane@no-domain.com
3: jane@no-domain.com
4: jane@no-domain.com
5: jane@no-domain.com
6: jane@no-domain.com
7: jane@no-domain.com
8: jane@no-domain.com
9: jane@no-domain.com
...但是当我添加 .parallel()
它抛出一个 NullPointerException
因为不知何故模拟用户不存在。
当我使用 ExecutorService
或某种线程时,它的行为方式相同。
那么,如何并行执行该方法?
使用 .parallel()
将在流上并行生成多个线程 运行。来自 tutorial 上的流:
When a stream executes in parallel, the Java runtime partitions the
stream into multiple substreams. Aggregate operations iterate over and
process these substreams in parallel and then combine the results.
因此您需要让子线程从本地线程继承 SecurityContextHolder
,为此您可以使用 @PostConstruct
,如下所示,(请参阅更多信息 here):
@SpringBootTest
@EnableAsync
@WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"})
public class NonTest {
@Test
public void test() {
IntStream.range(1, 10)
.parallel()
.forEach(this::getUser);
}
@PostConstruct
void setGlobalSecurityContext() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
public void getUser(int value) {
System.out.println(value + ": " + ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
}
}
输出
6: jane@no-domain.com
7: jane@no-domain.com
1: jane@no-domain.com
2: jane@no-domain.com
3: jane@no-domain.com
9: jane@no-domain.com
4: jane@no-domain.com
8: jane@no-domain.com
5: jane@no-domain.com
下面的代码只是我想要实现的一个简化版本。
我正在使用 @WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"})
在我的测试中模拟用户。
@RunWith(SpringRunner.class)
@SpringBootTest
@EnableAsync
@WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"})
public class NonTest {
@Test
public void test() {
IntStream.range(1, 10)
.parallel() // Comment this and it will work
.forEach(value -> {
getUser(value);
});
}
public void getUser(int value) {
System.out.println(value + ": " + ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
}
}
当我执行上面的代码并注释掉 .parallel()
时,它可以正常工作并按预期打印出以下内容:
1: jane@no-domain.com
2: jane@no-domain.com
3: jane@no-domain.com
4: jane@no-domain.com
5: jane@no-domain.com
6: jane@no-domain.com
7: jane@no-domain.com
8: jane@no-domain.com
9: jane@no-domain.com
...但是当我添加 .parallel()
它抛出一个 NullPointerException
因为不知何故模拟用户不存在。
当我使用 ExecutorService
或某种线程时,它的行为方式相同。
那么,如何并行执行该方法?
使用 .parallel()
将在流上并行生成多个线程 运行。来自 tutorial 上的流:
When a stream executes in parallel, the Java runtime partitions the stream into multiple substreams. Aggregate operations iterate over and process these substreams in parallel and then combine the results.
因此您需要让子线程从本地线程继承 SecurityContextHolder
,为此您可以使用 @PostConstruct
,如下所示,(请参阅更多信息 here):
@SpringBootTest
@EnableAsync
@WithMockUser(username = "jane@no-domain.com", authorities = {"ROLE_ADMIN"})
public class NonTest {
@Test
public void test() {
IntStream.range(1, 10)
.parallel()
.forEach(this::getUser);
}
@PostConstruct
void setGlobalSecurityContext() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
public void getUser(int value) {
System.out.println(value + ": " + ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername());
}
}
输出
6: jane@no-domain.com
7: jane@no-domain.com
1: jane@no-domain.com
2: jane@no-domain.com
3: jane@no-domain.com
9: jane@no-domain.com
4: jane@no-domain.com
8: jane@no-domain.com
5: jane@no-domain.com