在Java 8中使用带有ParallelStream的HttpServletRequest

时间:2018-01-16 02:44:57

标签: java spring-security parallel-processing java-8 java-stream

我有一个简单的应用程序,它将公开一个名为'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)

1 个答案:

答案 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源代码可能会为理解带来额外的价值。