回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常

最近线上项目的关键环节突然抛出了一个数据库访问的空指针异常,不免神经一紧,经过最终排查,确认了该问题属于mybatis 3.3.0(不包含)以下版本已存在的bug,新版本的mybatis已经修复,具体情况如下:
系统jar版本
在这里插入图片描述
异常堆栈
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第1张图片

排查过程

  1. 根据堆栈信息,初步确定是mybatis的解析执行异常,通过搜索引擎进一步查证,该问题属于mybatis低版本中存在的并发bug,问题发生时也确实存在多个线程并发访问同一sql
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第2张图片
    需进一步确认具体原因。
  2. 由于之前对mybatis执行过程中mapper文件中定义的mappedStatement的sql语句的解析构建及参数绑定了解不够充分,故结合日志中的异常sql语句,在dev环境进行模拟,熟悉执行流程:

项目启动阶段进行mapper文件的解析,构建XML的结构化对象

org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode解析statement, 并将结果维护在org.apache.ibatis.session.Configuration 的statementMap中,用于sql执行时依据statementId获取对应的statement
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第3张图片
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第4张图片

org.apache.ibatis.mapping.MappedStatement 结构,包含参数类型,结果类型,sql解析后的对象

回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第5张图片
org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode 解析sqlSource
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第6张图片
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第7张图片

  • sql执行解析阶段,根据statementId及参数选择相应的statement进行解析执行
    执行堆栈如下
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第8张图片
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第9张图片
    org.apache.ibatis.session.Configuration#getMappedStatement(java.lang.String, boolean) 执行时获取对应的statement对象
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第10张图片
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第11张图片
    根据statement中sqlSource对象及参数信息,对sql语句进行解析构建
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第12张图片
    org.apache.ibatis.scripting.xmltags.MixedSqlNode#apply 解析where语句时(在此过程中出现的问题)
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第13张图片
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第14张图片
    org.apache.ibatis.scripting.xmltags.IfSqlNode#apply 解析if语句
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第15张图片
    org.apache.ibatis.scripting.xmltags.ExpressionEvaluator#evaluateBoolean 通过ognl来解析表达式,如不等,大于等等

回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第16张图片

org.apache.ibatis.scripting.xmltags.OgnlCache#getValue 通过OgnlParser解析表达式(” settleDate != null”), 相同的表达式OgnlParser会单独创建一个,并对结果进行缓存,故此处无并发问题
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第17张图片
org.apache.ibatis.ognl.SimpleNode#getValue ognl获取值的处理
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第18张图片
org.apache.ibatis.ognl.SimpleNode#evaluateGetValueBody 当前表达式解析过程中,表达式” settleDate”属于ASTConst类型,且多线程并发执行解析该表达式时,共用同一个OgnlParser解析的结果,故下图部分可能存在并发问题,导致其中某个线程获取到的contantValue为null
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第19张图片
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第20张图片
Mybatis 3.3.0版本已对此处进行修改,解决了此处问题如下:
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第21张图片
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第22张图片
同时,通过堆栈溯源导致NPE是由于获取property(即String类型”settleDate”)值为空导致 org.apache.ibatis.ognl.ASTProperty#getValueBody

回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第23张图片

org.apache.ibatis.ognl.ASTNotEq#getValueBody 执行到此处不等式,解析完毕
回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第24张图片

  • 执行查询阶段
    org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler) 执行查询
    在这里插入图片描述
    org.apache.ibatis.mapping.MappedStatement#getBoundSql 执行解析
    回想那天让我虎躯一震的bug:Mybatis Ognl引起的异常_第25张图片
  1. 通过上述步骤,及对OgnlParser等排除,最终确认并发的问题点

参考内容

Mybatis中问题记录

https://github.com/mybatis/mybatis-3/issues/224
https://issues.apache.org/jira/browse/OGNL-121

Mybatis各版本发布日志

https://github.com/mybatis/mybatis-3/releases
https://github.com/mybatis/mybatis-3/issues?q=milestone%3A3.3.0

Struts2.1.8 Ognl 漏洞浅析和解决方案

https://blog.csdn.net/xlxxcc/article/details/52244965

看到这里的小伙伴,如果你喜欢这篇文章的话,别忘了转发、收藏、留言互动!

最近我新整理了一些Java资料,包含个人精选的Java架构学习视频、大厂实战知识点、大厂内部面试题,如果你需要的话,欢迎私信我

如果对文章有任何问题,欢迎在留言区和我交流~

你可能感兴趣的:(实战,经验分享,程序人生,其他)