今天在一个现有的SpringMVC的Web程序中添加新功能,引起了NullPoint的错误。看了代码,找到了错误的发生点,怎么看,代码都是正确的。不知道为啥?
起因是这样的:
我在SpringMVC中添加到了controller,然后写了service类。里面涉及了同事的dao类。在service类中的一个查询方法中,调用了2个dao中的查询数据的方法。因为数据量大,为了加快程序的运行速度,提高程序的运行效率,我在servcie的这个查询方法中对这个2个dao的查询分别写了Callable 接口的并行查询操作。写好之后,发现了上面说的空指针的错误。当我把它们修改为线性的单线程查询的时候(一个查询方法执行完毕,再去执行另一个查询方法),发现没有空指针的错误。百思不得其解。我查看了同事的dao类,发现这个null的空指针错误就是代码里面调用了spring security的一个用户权限检查的方法引起的。我又再次尝试单线程调用,是没有问题,采用多线程调用就有问题。(我在servcie类里面用ExecutorService exec = Executors.newCachedThreadPool(); 生成的新的线程池)。想了想,明白了。同事的dao类,调用spring security的权限检查,里面的spring应该是把当前运行的线程(tomcat产生的线程) 和一个用户的权限绑定了在一起,采用ThreadLocal。我利用自己的线程,造成ThreadLocal找不到新线程的用户组绑定内容,出现了空指针。
解决方案:
我调用同事的dao类的2个查询方法,一个涉及到spring security,一个没有涉及到spring security。我把没有涉及到spring security的查询方法放在自己创建的线程池中调用,涉及到security的方法,保留在原有线程中(tomcat生成的线程),这样,即取到了并发查询的效果,也没有干涉到spring security 的本地线程变量绑定。
思考:使用spring的时候,需要注意,尤其多线程的情况下,改变了当前线程环境的情况下,需要考虑spring的线程环境是否发生了改变。
修改后的代码:
public String initQueryCondition(final Integer gameId) {
try {
Map<String, Object> dataMap = new HashMap<String, Object>();
// 开始并行查询
ExecutorService exec = Executors.newCachedThreadPool();
Future<List<GameServerBean>> f1 = exec
.submit(new Callable<List<GameServerBean>>() {
@Override
public List<GameServerBean> call() throws Exception {
return gameServerDao.selectAll(gameId);
}
});
exec.shutdown();
/**
*
* 这里没有用并行查询,因为这个查询里面涉及了ThreadLocal(由spring的security使用),
* 启动新的线程会造成线程环境改变,发生null的错误
*/
List<GameProviderBean> providerList = gameProviderDao
.selectAll(gameId);
List<GameServerBean> serverList = f1.get();
dataMap.put("serverLsit", serverList);
dataMap.put("providerList", providerList);
return createJson(dataMap);
} catch (Exception e) {
LOGGER.error("Error:" + e.getMessage(), e);
}
return null;
}