我有一个简单的应用程序,它将公开一个名为'getAllDeviceData'
的RESTFul GET端点,该端点只返回数据库中设备表中所有设备的获取数据列表。
对于每个请求,我通过验证HttpServletRequest.getUserPrincipal()
方法来验证用户身份。
为了加快这个过程,我使用了带有lambda表达式的parallelStream。
在ParallelStream中,我正在调用另一个名为'getDeviceData'
的方法,其中我正在进行身份验证并从数据库中获取数据。
问题是,当并行流进程调用getDeviceDat
方法时,我收到NullPointer异常并且无法完成并行流。
值得注意的是,HttpServletRequest.getUserPrincipal()
在方法中为空。但它实际上存在于'getAllDeviceData'
(lambda表达式所在的位置)。
如果我将'parallelStream()'
替换为'stream()'
,则无任何问题,但在这种情况下并行性质不存在。
@Override
@ResponseBody
@RequestMapping(value = "getAllDeviceData", method = RequestMethod.GET, consumes = "*")
public List<List<Data>> getAllDeviceData(
@RequestParam(value = "recordLimit", required = false) final Integer recordLimit,
final HttpServletRequest request) {
final List<Device> deviceList = deviceService.getAllDevices();
final List<List<Data>> dataList = deviceList.parallelStream().map(device -> getDeviceData(recordLimit, device.getDeviceId(), request)).collect(Collectors.toList());
return alerts;
}
private List<Data> getDeviceData(@RequestParam(value = "recordLimit", required = false) Integer recordLimit, String deviceId, HttpServletRequest request) {
if(request.getUserPrincipal() == null){
logger.info("User Principle Null - 1");
}else {
logger.info("User Principle Not Null - 1");
}
authService.doAuthenticate(request);
// if authrnticated proceed with following...
List<Data> deviceData = deviceService.getGetDeviceData(deviceId);
return deviceData;
}
然而,我观察到了一些事情。
请查看上述申请的以下日志(已省略不必要的部分)。
在其中,主线程(例如:http-nio-7070-exec-2
等 - 这是该应用程序服务器的线程池的主线程)工作正常,因为它打印出“用户原则不是空” - 1&#39;但是,在ForkJoinPool.commonPool-worker-2
等并行流的细分线程中,HTTPServletRequest.getUserPrincipal()
变为空。
2018-01-15 15:28:06,897 INFO [http-nio-7070-exec-2] User Principle Not Null - 1
2018-01-15 15:28:06,897 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1
2018-01-15 15:28:06,906 INFO [ForkJoinPool.commonPool-worker-3] User Principle Null - 1
2018-01-15 15:28:06,955 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1
2018-01-15 15:28:06,955 INFO [ForkJoinPool.commonPool-worker-1] User Principle Null - 1
2018-01-15 15:28:06,957 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1
2018-01-15 15:28:06,959 INFO [ForkJoinPool.commonPool-worker-3] User Principle Null - 1
2018-01-15 15:28:07,064 INFO [ForkJoinPool.commonPool-worker-2] User Principle Null - 1
2018-01-15 15:28:07,076 INFO [http-nio-7070-exec-2] User Principle Not Null -1
2018-01-15 15:28:07,078 INFO [ForkJoinPool.commonPool-worker-1] User Principle Null - 1
我还是lambda表达式和并行流的新手。 请帮我理解这里的问题。
Java详细信息:
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
答案 0 :(得分:2)
根本原因是Spring将SecurityContextHolderAwareRequestWrapper实例注入到您的方法中。调用equest.getUserPrincipal()
时,此包装器调用以下行:
private Authentication getAuthentication() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder有不同的策略。默认使用MODE_THREADLOCAL
策略。这就是为什么你在主线程中有用户原则但在forkjoinpool线程中没有用户原则的原因。
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
VM选项是解决您的问题的方法。 InheritableThreadLocal javadoc和InheritableThreadLocalSecurityContextHolderStrategy源代码可能会为理解带来额外的价值。