我有一个奇怪的问题。
鉴于此代码:
@RequestMapping(value = {"/randomizer"}, method = RequestMethod.POST)
public CompletableFuture<String> randomizer(){
CompletableFuture<String> someString = stringService
.findRandomByInput("123")
.thenCombine(stringService.findAnotherRandomByInput("321"), (result1, result2) -> {
return applyRandom(result1, result2);
});
CompletableFuture<Void> computation = computingService.computeRandomByInput(RandomDto.empty(), "123");
return someString.thenCombineAsync(computation, (result1, result2) -> {
combineIt(result1, result2, getCurrentApplicationUser());
}, taskExecutor);
}
通过调用
getCurrentApplicationUser()
我正在访问 spring 的 SecurityContextHolder.getContext().getAuthentication()
接口。
我有这个任务执行者:
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(6);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("DmpkApplication-");
executor.initialize();
// the following is necessary because of:
// https://stackoverflow.com/a/57434013/7320372
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
所以问题是:
我调用上述
randomizer
控制器 5 次,到第 6 次时,getAuthentication()
调用为 null
。
有时对控制器的第一次调用会产生
null
,并且所有其他后续调用都会起作用。
我不知道这里出了什么问题。
我自己找到了解决办法。
我重命名了上面的bean
public Executor taskExecutor()
以下:
public Executor executor()
现在问题已经解决了。我认为 Spring 本身已经定义了一个
executor
。之前的taskExecutor
是另外一个。但不知怎的,Spring 的 executor
被调用了,默认情况下它不是 DelegatingSecurityContextAsyncTaskExecutor
。
所以也许这就是问题所在。
没关系 - 现在可以了!
我在 Spring Boot 中遵循了以下步骤,我们还需要小心,因为代码正在关闭执行程序服务,并且一旦上下文关闭,就不会创建任何内存泄漏。
第 1 步 - 创建一个配置类,如下所示。
@Configuration
public class EnableSecurityCtxForThreadPool {
@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(15);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("async-");
//executor.initialize();
// this is not need as stated above because ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
which again extends InitializingBean which call this method automatically using @Overidded afterPropertiesSet method
return executor;
}
@Bean
public DelegatingSecurityContextAsyncTaskExecutor taskExecutor
(ThreadPoolTaskExecutor delegate) {
return new DelegatingSecurityContextAsyncTaskExecutor(delegate);
}
}
第2步:创建一个实用程序类,以便我们可以在代码中获取
DelegatingSecurityContextAsyncTaskExecutor
的bean,而不是自动装配ApplicationContext。
@Configuration
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(String beanName, Class<T> clazz) {
return context.getBean(beanName, clazz);
}
}
第 3 步 - 创建一个像这样的 completableFuture,以便池中的所有线程都已经设置了安全上下文。
CompletableFuture<String> stringCF = CompletableFuture.supplyAsync(() -> "Hello JayZ", SpringUtils.getBean("taskExecutor", Executor.class));