当前位置: 首页 > 科技观察

警惕,Mybatis的Size()方法有陷阱!_0

时间:2023-03-23 11:57:39 科技观察

本文转载自微信公众号“小明菜市场”,可通过以下二维码关注。转载本文请联系小明菜市场公众号。你好!我是晓晓,今天又见面了。今天的主要内容是使用MyBatis的size方法的主要注意事项。前言MyBatis是一个开源的轻量级半自动面向对象和关系数据库映射的ORM框架,其中xml文件结合sql语句,最大的特点是应用sql的解耦。OGNL表达式,广泛应用于MyBatis,是一种EL语言,用于设置和获取Java对象的属性,可以投影列表和执行lambda表达式。ognl提供了简单易行的ognl表达式。在线服务经常会出现异常,在构造各种空的OGNL表达式时会重现异常。具体堆栈信息如下:###Errorqueryingdatabase.Cause:org.apache.ibatis.builder.BuilderException:Errorrevaluatingexpression'list!=nullandlist.size()>0'.Cause:org.apache.ibatis.ognl.MethodFailedException:Method"size"failedforobject[1][java.lang.IllegalAccessException:Classorg.apache.ibatis.ognl.OgnlRuntimecannotaccessamemberofclassjava.util.Collections$SingletonListwithmodifiers"public"]###Cause:org.apache.ibatis.builder.BuilderException:Errorrevaluatingexpression'list!=nullandlist.size()>0'。原因:org.apache.ibatis.ognl.MethodFailedException:对象[1]的方法“大小”失败[java.lang.IllegalAccessException:Classorg.apache.ibatis.ognl.OgnlRuntimecannotaccessamemberofclassjava.util.Collections$SingletonListwithmodifiers"public"]atorg.apache.ibatis.exceptions.ExceptionFactory.wrapException.java:23)org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:107)atorg.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98)atcn.com.shaobingmm.MybatisBugTest$2.run(MybatisBugTest.java:88)atjava.lang.Thread.run(Thread.java:745)Causedby:org.apache.ibatis.builder.BuilderException:Errorevaluatingexpression'list!=nullandlist.size()>0'.Cause:org.apache.ibatis.ognl.MethodFailedException:Method"size"failedforobject[1][java.lang.IllegalAccessException:Classorg.apache.ibatis.ognl.OgnlRuntimecannotaccessamemberofclassjava.util.Collections$SingletonListwithmodifiers"public"]atorg.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.javaat:47)atorg.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:29)atorg.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:30)atorg.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29)atorg.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:51)atorg.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29)atorg.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:37)atorg.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:275)atorg.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:79)atorg.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104)...3moreCausedby:org.apache.ibatis.ognl.MethodFailedException:Method"size"failedforobject[1][java.lang.IllegalAccessException:Classorg.apache.ibatis.ognl.OgnlRuntimecannotaccessamemberofclassjava.util.Collections$SingletonListwithmodifiers"public"]atorg.apache.ibatis.ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:837)atorg.apache.ibatis.ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:61)atorg.apache.ibatis.ognl.OgnlRuntime.callMethod(OgnlRuntime.java:860)atorg.apache.ibatis.ognl.ASTMethod.getValueBody(ASTMethod.java:73)atorg.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)atorg.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:170)210)atorg.apache.ibatis.ognl.ASTChain.getValueBody(ASTChain.java:109)atorg.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)atorg.apache.ibatis.ognl.SimpleNode。getValue(SimpleNode.java:210)atorg.apache.ibatis.ognl.ASTGreater.getValueBody(ASTGreater.java:49)atorg.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)atorg.apache.ibatis。ognl。SimpleNode.getValue(SimpleNode.java:210)atorg.apache.ibatis.ognl.ASTAnd.getValueBody(ASTAnd.java:56)atorg.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)atorg.apache。ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210)atorg.apache.ibatis.ognl.Ognl.getValue(Ognl.java:333)atorg.apache.ibatis.ognl.Ognl.getValue(Ognl.java:413)atorg.apache.ibatis.ognl.Ognl.getValue(Ognl.java:395)atorg.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:45)...12moreList的大小方法显然是公开的,目前还无法访问。该异常在测试环境中没有重复出现过,但是接口完整调用链中的错误次数占总调用次数的0.01%,属于概率事件模拟测试编写模拟多线程并发读取公司列表的测试代码select*fromcompany0">andidin#{id}多线程下的压力测试().build(in);finalListids=Collections.singletonList(1L);finalSqlSessionsession=sqlSessionFactory.openSession();finalCountDownLatchmCountDownLatch=newCountDownLatch(1);for(inti=0;i<50;i++){Threadthread=newThread(newRunnable(){publicvoidrun(){try{mCountDownLatch.await();}catch(InterruptedExceptione){e.printStackTrace();}for(intk=0;k<100;k++){session.selectList("CompanyMapper.getCompanysByIds",ids);}}});thread.start();}mCountDownLatch.countDown();同步(MybatisBugTest.class){try{MybatisBugTest.class.wait();}catch(InterruptedExceptione){e.printStackTrace();}}}catch(IOExceptione){e.printStackTrace();}catch(Throwablee){e.printStackTrace();}finally{if(in!=null)try{in.close();}catch(IOExceptione){e.printStackTrace();}}以上代码并发时会出现异常Causedby:org.apache.ibatis.ognl.MethodFailedException:对象[1]的方法“大小”失败[java.lang.IllegalAccessException:Classorg.apache.ibatis.ognl.OgnlRuntimecannotaccessamemberofclassjava.util.Collections$SingletonListwithmodifiers“public”]atorg.apache.apache。ibatis.ognl.Ognl。.callAppropriateMethod(OgnlRuntime.java:837)异常信息提示ognlRuntime类无法访问查看源码,解决java.util.Collections的私有成员SingletonList。查看源码可以知道锁在invokeMethod方法上。publicstaticObjectcallAppropriateMethod(OgnlContextcontext,Objectsource,Objecttarget,StringmethodName,StringpropertyName,Listmethods,Object[]args)throwsMethodFailedException{Objectreason=null;Object[]actualArgs=objectArrayPool.create(args.length);try{Methode=getAppropriateMethod(context,source,target),methodName,propertyName,methods,args,actualArgs);if(e==null||!isMethodAccessible(context,source,e,propertyName)){StringBufferbuffer=newStringBuffer();if(args!=null){inti=0;for(intilast=args.length-1;i<=ilast;++i){Objectarg=args[i];buffer.append(arg==null?NULL_STRING:arg.getClass().getName());if(i