一个mybatis的bug追踪
1、背景
注意:这个bug在mybatis的 3.4.0版本中出现
前一段时间参与一个项目的开发,在排查一个功能问题的时候发现一部分机器的业务日志中存在少量的mybatis异常,主要异常信息如下:
“ Error evaluating expression 'criteria.valid'. Cause: org.apache.ibatis.ognl.NoSuchPropertyException: criteria.valid ”
而这一代码存在的位置是实体类对应的 mapper的xml配置文件中,我看了下就是简单的布尔判断,如果有问题的话早就报出来才对,在github的mybatis项目bug处找到了一些线索提示可能是并发导致的,然后仔细分析了机器的日志,发现当时的日志量特别大,和相关同学确认了一下当时在进行压测,大并发的情况下出现问题和mybatis项目bug的提示相符合,下面进行具体的问题排查。
2、追踪过程:
从错误信息中可以看出来是ognl分析查询条件出了问题,本文主要讲述这个问题的原因及排查问题的主要代码,ognl的相关内容以及mybatis如何使用ognl不是本文的内容。
步骤一、找到这个类发现这个调用是一个对象的方法调用,判断是否有查询条件。
步骤二、既然是方法调用,那就查看ognl的方法调用过程,这个方法所在类:
org.apache.ibatis.ognl.OgnlRuntime
主要用到的方法:
这个方法内部主要是通过红线圈住的两个地方来获取该方法,其实就是两个方法,一个方法是
getGetMethod(参数自己看哈),另一个方法是getReadMethod(参数自己看),注意看getReadMethod方法调用传入的最后一个参数0,这个比较重要,后面会分析。
跟踪进入方法getGetMethod:
现在我们来分析一下这个方法返回null的原因,当同时有两个线程同时进入方法getGetMethod
内而且从map中取得的值都是null的时候,也就是这一步
Method method = cacheGetMethod.get(targetClass, propertyName);
返回的都是null的时候两个线程都进入到下面的代码,当一个线程执行了下面红线圈起来的地方,cacheGetMethod.put(targetClass, propertyName, method);,之后另一个线程执行了
else if (cacheGetMethod.containsKey(targetClass, propertyName)) {
return null;
}
所以这一个线程从该方法取得的method=null。那么根据图一,下一步执行到方法
getReadMethod中,下面分析这个方法:
注意我们上面说到的,当numParams=0的时候,会跳过方法的中间大部分代码而进入到最下面的代码,当第一次进来的时候,传进来的方法名没有以get开头,那么会把这个方法名和get字符串拼接起来,递归调用getReadMethod,然后还是进入到最下面的部分,method=null,而且这次方法名是以get开头,那么根据条件会返回null, 也就是说,如果传入的参数numParams=0,那么这个方法永远返回的都是null, 至此可以分析得到的结果是,mybatis自身在高并发的情况下获取方法的代码不是线程安全的。
解决办法:把mybatis版本升级到最新版本,我升级到版本 3.4.6就没有这个问题了,由于篇幅原因这里不再介绍3.4.6是如何解决这个问题的,大家感兴趣可以自己去研究一下,以上分析,如有不当,欢迎指正,谢谢。
网友评论