成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

警惕,Mybatis的Size()方法竟然有坑!

運維 數據庫運維
MyBatis 是一個開源的輕量級的半自動化的 ORM 框架,用于面向對象和關系型數據庫的映射,其中 xml 文件,和sql語句結合,最大的特點,應用程序sql解耦。

[[350041]]

本文轉載自微信公眾號「小明菜市場」,可以通過以下二維碼關注。轉載本文請聯系小明菜市場公眾號。

Hi ! 我是小小,今天我們又見面了,今日的主要內容是MyBatis的size方法使用的主要的注意事項。

前言

MyBatis 是一個開源的輕量級的半自動化的 ORM 框架,用于面向對象和關系型數據庫的映射,其中 xml 文件,和sql語句結合,最大的特點,應用程序sql解耦。OGNL表達式,是MyBatis中的廣泛應用,是一種EL語言,用于設置和獲取 Java 對象的屬性,并且可以對列表進行投影和執行lambda表達式,ognl提供了簡單,便于執行的ognl表達式。一個線上服務,經常會出現一個異常,構造各種OGNL表達式為空的情況都會重現該異常,具體的堆棧信息如下:

  1. ### Error querying database.  Cause: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'list != null and list.size() > 0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"
  2. ### Cause: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'list != null and list.size() > 0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"
  3.     at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23) org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:107) 
  4.     at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98) 
  5.     at cn.com.shaobingmm.MybatisBugTest$2.run(MybatisBugTest.java:88) 
  6.     at java.lang.Thread.run(Thread.java:745) 
  7. Caused by: org.apache.ibatis.builder.BuilderException: Error evaluating expression 'list != null and list.size() > 0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"
  8.     at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java 
  9.     at:47) 
  10.     at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:29) 
  11.     at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:30) 
  12.     at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29) 
  13.     at org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:51) 
  14.     at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29) 
  15.     at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:37) 
  16.     at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:275) 
  17.     at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:79) 
  18.     at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104) 
  19.     ... 3 more 
  20. Caused by: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"
  21.     at org.apache.ibatis.ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:837) 
  22.     at org.apache.ibatis.ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:61) 
  23.     at org.apache.ibatis.ognl.OgnlRuntime.callMethod(OgnlRuntime.java:860) 
  24.     at org.apache.ibatis.ognl.ASTMethod.getValueBody(ASTMethod.java:73) 
  25.     at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170) 
  26.     at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210) 
  27.     at org.apache.ibatis.ognl.ASTChain.getValueBody(ASTChain.java:109) 
  28.     at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170) 
  29.     at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210) 
  30.     at org.apache.ibatis.ognl.ASTGreater.getValueBody(ASTGreater.java:49) 
  31.     at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170) 
  32.     at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210) 
  33.     at org.apache.ibatis.ognl.ASTAnd.getValueBody(ASTAnd.java:56) 
  34.     at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170) 
  35.     at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210) 
  36.     at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:333) 
  37.     at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:413) 
  38.     at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:395) 
  39.     at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:45) 
  40.     ... 12 more 

List的size方法明明有public,還不可訪問,該異常在測試環境未重現,但是在接口的完整調用鏈路中出錯的次數占總的調用次數的0.01%,這是概率性事件。

模擬測試

編寫模擬多線程并發讀取公司列表的測試代碼

  1. <mapper namespace="CompanyMapper"
  2.     <select id="getCompanysByIds"resultType="cn.com.shaobingmm.Company"
  3.         select * 
  4.         from company 
  5.         <where
  6.             <if test="list != null and list.size() > 0"
  7.                 and id in 
  8.        <foreach collection="list" item="id" open="(" separator="," close=")">#{id} 
  9. </foreach> 
  10.             </if> 
  11.         </where
  12.     </select
  13. </mapper> 

多線程下進行壓力測試

  1. String resource = "mybatis-config.xml"
  2.         InputStream in = null
  3.         try { 
  4.             in = Resources.getResourceAsStream(resource); 
  5.             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); 
  6.             final List<Long> ids = Collections.singletonList(1L); 
  7.             final SqlSession session = sqlSessionFactory.openSession(); 
  8.             final CountDownLatch mCountDownLatch = new CountDownLatch(1); 
  9.             for (int i = 0; i < 50; i++) { 
  10.                 Thread thread = new Thread(new Runnable() { 
  11.                     public void run() { 
  12.                         try { 
  13.                             mCountDownLatch.await(); 
  14.                         } catch (InterruptedException e) { 
  15.                             e.printStackTrace(); 
  16.                         } 
  17.                         for (int k = 0; k < 100; k++) { 
  18.                             session.selectList("CompanyMapper.getCompanysByIds", ids); 
  19.                         } 
  20.                     } 
  21.                 }); 
  22.                 thread.start(); 
  23.             } 
  24.             mCountDownLatch.countDown(); 
  25.             synchronized (MybatisBugTest.class) { 
  26.                 try { 
  27.                     MybatisBugTest.class.wait(); 
  28.                 } catch (InterruptedException e) { 
  29.                     e.printStackTrace(); 
  30.                 } 
  31.             } 
  32.  
  33.         } catch (IOException e) { 
  34.             e.printStackTrace(); 
  35.         } catch (Throwable e) { 
  36.             e.printStackTrace(); 
  37.         } finally { 
  38.             if (in != null
  39.                 try { 
  40.                     in.close(); 
  41.                 } catch (IOException e) { 
  42.                     e.printStackTrace(); 
  43.                 } 
  44.         } 

上述代碼在并發的時候會出現異常。

  1. Caused by: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object [1] [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class java.util.Collections$SingletonList with modifiers "public"
  2.     at org.apache.ibatis.ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:837) 

異常信息表明ognlRuntime類不能訪問

查看源碼,破案

java.util.Collections的私有成員SingletonList。查看源代碼,可以知道鎖定在invokeMethod方法上。

  1. public static Object callAppropriateMethod(OgnlContext context, Object source, Object target, String methodName, String propertyName, List methods, Object[] args) throws MethodFailedException { 
  2.         Object reason = null
  3.         Object[] actualArgs = objectArrayPool.create(args.length); 
  4.  
  5.         try { 
  6.             Method e = getAppropriateMethod(context, source, target, methodName, propertyName, methods, args, actualArgs); 
  7.             if(e == null || !isMethodAccessible(context, source, e, propertyName)) { 
  8.                 StringBuffer buffer = new StringBuffer(); 
  9.                 if(args != null) { 
  10.                     int i = 0; 
  11.  
  12.                     for(int ilast = args.length - 1; i <= ilast; ++i) { 
  13.                         Object arg = args[i]; 
  14.                         buffer.append(arg == null?NULL_STRING:arg.getClass().getName()); 
  15.                         if(i < ilast) { 
  16.                             buffer.append(", "); 
  17.                         } 
  18.                     } 
  19.                 } 
  20.  
  21.                 throw new NoSuchMethodException(methodName + "(" + buffer + ")"); 
  22.             } 
  23.  
  24.             Object var14 = invokeMethod(target, e, actualArgs); 
  25.             return var14; 
  26.         } catch (NoSuchMethodException var21) { 
  27.             reason = var21; 
  28.         } catch (IllegalAccessException var22) { 
  29.             reason = var22; 
  30.         } catch (InvocationTargetException var23) { 
  31.             reason = var23.getTargetException(); 
  32.         } finally { 
  33.             objectArrayPool.recycle(actualArgs); 
  34.         } 
  35.  
  36.         throw new MethodFailedException(source, methodName, (Throwable)reason); 
  37.     } 

其方法代碼

  1. public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException { 
  2.         boolean wasAccessible = true
  3.         if(securityManager != null) { 
  4.             try { 
  5.                 securityManager.checkPermission(getPermission(method)); 
  6.             } catch (SecurityException var6) { 
  7.                 throw new IllegalAccessException("Method [" + method + "] cannot be accessed."); 
  8.             } 
  9.         } 
  10.  
  11.         if((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !(wasAccessible = method.isAccessible())) { 
  12.             method.setAccessible(true); (1) 
  13.         } 
  14.  
  15.         Object result = method.invoke(target, argsArray); (3) 
  16.         if(!wasAccessible) { 
  17.             method.setAccessible(false); (2) 
  18.         } 
  19.  
  20.         return result; 
  21.     } 

問題出現在meta是一個共享變量,即

  1. public int java.util.Collections$SingletonList.size() 

當,第一個線程t1到第一行代碼允許method方法可以調用,第二個線程t2,執行到2把方法method設置為不可訪問,接著t1又執行,此時行列3會發生異常。

升級版本

lgnl2.7,已經修復了這個問題,所以修復后的代碼如下

  1. public static Object invokeMethod(Object target, Method method, Object[] argsArray) throws InvocationTargetException, IllegalAccessException { 
  2.         boolean syncInvoke = false
  3.         boolean checkPermission = false
  4.         int mHash = method.hashCode(); 
  5.         synchronized(method) { 
  6.             if(_methodAccessCache.get(Integer.valueOf(mHash)) == null || _methodAccessCache.get(Integer.valueOf(mHash)) == Boolean.TRUE) { 
  7.                 syncInvoke = true
  8.             } 
  9.  
  10.             if(_securityManager != null && _methodPermCache.get(Integer.valueOf(mHash)) == null || _methodPermCache.get(Integer.valueOf(mHash)) == Boolean.FALSE) { 
  11.                 checkPermission = true
  12.             } 
  13.         } 
  14.  
  15.         boolean wasAccessible = true
  16.         Object result; 
  17.         if(syncInvoke) { 
  18.             synchronized(method) { 
  19.                 if(checkPermission) { 
  20.                     try { 
  21.                         _securityManager.checkPermission(getPermission(method)); 
  22.                         _methodPermCache.put(Integer.valueOf(mHash), Boolean.TRUE); 
  23.                     } catch (SecurityException var12) { 
  24.                         _methodPermCache.put(Integer.valueOf(mHash), Boolean.FALSE); 
  25.                         throw new IllegalAccessException("Method [" + method + "] cannot be accessed."); 
  26.                     } 
  27.                 } 
  28.  
  29.                 if(Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(method.getDeclaringClass().getModifiers())) { 
  30.                     _methodAccessCache.put(Integer.valueOf(mHash), Boolean.FALSE); 
  31.                 } else if(!(wasAccessible = method.isAccessible())) { 
  32.                     method.setAccessible(true); 
  33.                     _methodAccessCache.put(Integer.valueOf(mHash), Boolean.TRUE); 
  34.                 } else { 
  35.                     _methodAccessCache.put(Integer.valueOf(mHash), Boolean.FALSE); 
  36.                 } 
  37.  
  38.                 result = method.invoke(target, argsArray); 
  39.                 if(!wasAccessible) { 
  40.                     method.setAccessible(false); 
  41.                 } 
  42.             } 
  43.         } else { 
  44.             if(checkPermission) { 
  45.                 try { 
  46.                     _securityManager.checkPermission(getPermission(method)); 
  47.                     _methodPermCache.put(Integer.valueOf(mHash), Boolean.TRUE); 
  48.                 } catch (SecurityException var11) { 
  49.                     _methodPermCache.put(Integer.valueOf(mHash), Boolean.FALSE); 
  50.                     throw new IllegalAccessException("Method [" + method + "] cannot be accessed."); 
  51.                 } 
  52.             } 
  53.  
  54.             result = method.invoke(target, argsArray); 
  55.         } 
  56.  
  57.         return result; 
  58.     } 

關于作者

我是小小,一個生于二線,活在一線城市的程序猿,我是小小,我們下期再見。

 

責任編輯:武曉燕 來源: 小明菜市場
相關推薦

2019-09-18 15:20:16

MyBatisSQL數據庫

2020-07-07 07:37:36

Integer源碼Java

2022-09-04 12:43:03

算法裁員Meta

2021-07-05 18:05:40

SpringBean方法

2018-09-11 08:05:44

千兆路由器廠商

2022-02-14 12:04:43

前綴SpringJpa

2019-06-14 08:48:46

Tomcat日志SpringBoot

2015-07-20 15:26:56

WiFi感知

2020-11-27 09:16:21

BlockingQue

2021-12-08 08:30:55

Java AQS機制 Java 基礎

2016-06-07 09:23:05

瀏覽器技巧快捷鍵

2025-02-06 07:45:44

2021-05-07 05:34:25

Windows10操作系統微軟

2017-10-31 12:53:38

云計算虛擬化上云

2017-11-14 06:53:41

云計算云安全IT

2021-02-03 20:19:08

Istio流量網格

2020-11-02 08:35:59

內存數據庫Redis

2017-03-07 17:45:42

Windows磁盤碎片整理

2021-06-10 09:00:33

單例模式數據庫

2018-06-15 14:28:36

華為云
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 美日韩精品 | 国产一区二区欧美 | 九九久久免费视频 | 国产精品呻吟久久av凹凸 | 国产7777| 亚洲精品一区二区在线观看 | 狠狠操电影| 成人做爰www免费看视频网站 | 日韩不卡在线观看 | 午夜精品久久久久99蜜 | 伊人久久综合 | 精精精精xxxx免费视频 | www.日本精品 | 国产美女精品视频 | 亚洲欧洲在线视频 | 久久久久久久久久毛片 | 欧美一区二区三区电影 | 国产在线激情视频 | 欧美 日本 国产 | 亚洲欧美日韩电影 | 国产精品久久久久久久久久妇女 | 免费网站国产 | 国产一区二区久久久 | 中文字幕亚洲一区 | 一区二区三区欧美大片 | 亚洲国产精品久久久久 | 国产1页 | 国产亚洲欧美在线视频 | 亚洲视频区 | 伊人99| 亚洲精品一区国产精品 | 精品免费 | 亚洲午夜在线 | 一区二区三区电影网 | 日韩中文字幕 | 精品国产乱码久久久久久影片 | 精品国产乱码久久久久久a丨 | 亚洲欧洲成人av每日更新 | 精品综合久久 | 伦理二区| 神马久久av |