精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Mybatis超詳細(xì)插件機(jī)制解析,弄懂?dāng)r截器So easy

運維 數(shù)據(jù)庫運維
Mybatis采用責(zé)任鏈模式,通過動態(tài)代理組織多個插件(攔截器),通過這些插件可以改變Mybatis的默認(rèn)行為(諸如SQL重寫之類的),由于插件會深入到Mybatis的核心,因此在編寫自己的插件前最好了解下它的原理,以便寫出安全高效的插件。

[[285992]]

概述

Mybatis插件又稱攔截器,本篇文章中出現(xiàn)的攔截器都表示插件。

Mybatis采用責(zé)任鏈模式,通過動態(tài)代理組織多個插件(攔截器),通過這些插件可以改變Mybatis的默認(rèn)行為(諸如SQL重寫之類的),由于插件會深入到Mybatis的核心,因此在編寫自己的插件前最好了解下它的原理,以便寫出安全高效的插件。

MyBatis 允許你在已映射語句執(zhí)行過程中的某一點進(jìn)行攔截調(diào)用。默認(rèn)情況下,MyBatis 允許使用插件來攔截的方法調(diào)用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

總體概括為:

  • 攔截執(zhí)行器的方法
  • 攔截參數(shù)的處理
  • 攔截結(jié)果集的處理
  • 攔截Sql語法構(gòu)建的處理

Mybatis是通過動態(tài)代理的方式實現(xiàn)攔截的,閱讀此篇文章需要先對Java的動態(tài)代理機(jī)制有所了解。

Mybatis四大接口

既然Mybatis是對四大接口進(jìn)行攔截的,那我們先要知道Mybatis的四大接口是哪些: Executor, StatementHandler, ResultSetHandler, ParameterHandler。

 

Mybatis超詳細(xì)插件機(jī)制解析,弄懂?dāng)r截器So easy

 

上圖Mybatis框架的整個執(zhí)行過程。Mybatis插件能夠?qū)@四大對象進(jìn)行攔截,可以說包含到了Mybatis一次SQL執(zhí)行的所有操作。可見Mybatis的的插件很強(qiáng)大。

  1. Executor是 Mybatis的內(nèi)部執(zhí)行器,它負(fù)責(zé)調(diào)用StatementHandler操作數(shù)據(jù)庫,并把結(jié)果集通過 ResultSetHandler進(jìn)行自動映射,另外,他還處理了二級緩存的操作。從這里可以看出,我們也是可以通過插件來實現(xiàn)自定義的二級緩存的。
  2. StatementHandler是Mybatis直接和數(shù)據(jù)庫執(zhí)行sql腳本的對象。另外它也實現(xiàn)了Mybatis的一級緩存。這里,我們可以使用插件來實現(xiàn)對一級緩存的操作(禁用等等)。
  3. ParameterHandler是Mybatis實現(xiàn)Sql入?yún)⒃O(shè)置的對象。插件可以改變我們Sql的參數(shù)默認(rèn)設(shè)置。
  4. ResultSetHandler是Mybatis把ResultSet集合映射成POJO的接口對象。我們可以定義插件對Mybatis的結(jié)果集自動映射進(jìn)行修改。

插件Interceptor

Mybatis的插件實現(xiàn)要實現(xiàn)Interceptor接口,我們看下這個接口定義的方法。

  1. public interface Interceptor {   
  2.   Object intercept(Invocation invocation) throws Throwable;     
  3.   Object plugin(Object target);   
  4.   void setProperties(Properties properties); 

這個接口只聲明了三個方法:

  • setProperties方法是在Mybatis進(jìn)行配置插件的時候可以配置自定義相關(guān)屬性,即:接口實現(xiàn)對象的參數(shù)配置。
  • plugin方法是插件用于封裝目標(biāo)對象的,通過該方法我們可以返回目標(biāo)對象本身,也可以返回一個它的代理,可以決定是否要進(jìn)行攔截進(jìn)而決定要返回一個什么樣的目標(biāo)對象,官方提供了示例:return Plugin.wrap(target, this)。
  • intercept方法就是要進(jìn)行攔截的時候要執(zhí)行的方法。

理解這個接口的定義,先要知道java動態(tài)代理機(jī)制。plugin接口即返回參數(shù)target對象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理對象。在調(diào)用對應(yīng)對象的接口的時候,可以進(jìn)行攔截并處理。

Mybatis四大接口對象創(chuàng)建方法

Mybatis的插件是采用對四大接口的對象生成動態(tài)代理對象的方法來實現(xiàn)的。那么現(xiàn)在我們看下Mybatis是怎么創(chuàng)建這四大接口對象的。

  1. public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 
  2.   //確保ExecutorType不為空(defaultExecutorType有可能為空) 
  3.   executorType = executorType == null ? defaultExecutorType : executorType; 
  4.   executorType = executorType == null ? ExecutorType.SIMPLE : executorType; 
  5.   Executor executor;  if (ExecutorType.BATCH == executorType) { 
  6.    executor = new BatchExecutor(this, transaction); 
  7.   } else if (ExecutorType.REUSE == executorType) { 
  8.    executor = new ReuseExecutor(this, transaction); 
  9.   } else { 
  10.    executor = new SimpleExecutor(this, transaction); 
  11.   }  if (cacheEnabled) { 
  12.    executor = new CachingExecutor(executor); 
  13.   } 
  14.   executor = (Executor) interceptorChain.pluginAll(executor); 
  15.   return executor; 
  16.  
  17. public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 
  18.   StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 
  19.   statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); 
  20.   return statementHandler; 
  21.  
  22. public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { 
  23.   ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); 
  24.   parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); 
  25.   return parameterHandler; 
  26.  
  27. public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { 
  28.   ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); 
  29.   resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); 
  30.   return resultSetHandler; 

查看源碼可以發(fā)現(xiàn), Mybatis框架在創(chuàng)建好這四大接口對象的實例后,都會調(diào)用InterceptorChain.pluginAll()方法。InterceptorChain對象是插件執(zhí)行鏈對象,看源碼就知道里面維護(hù)了Mybatis配置的所有插件(Interceptor)對象。

  1. // target --> Executor/ParameterHandler/ResultSetHander/StatementHandler 
  2. public Object pluginAll(Object target) { 
  3.   for (Interceptor interceptor : interceptors) { 
  4.    target = interceptor.plugin(target); 
  5.   } 
  6.   return target; 

其實就是按順序執(zhí)行我們插件的plugin方法,一層一層返回我們原對象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理對象。當(dāng)我們調(diào)用四大接口的方法的時候,實際上是調(diào)用代理對象的相應(yīng)方法,代理對象又會調(diào)用四大接口的實例。

Plugin對象

我們知道,官方推薦插件實現(xiàn)plugin方法為:Plugin.wrap(target, this);

  1. public static Object wrap(Object target, Interceptor interceptor) { 
  2.   // 獲取插件的Intercepts注解 
  3.   Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); 
  4.   Class<?> type = target.getClass(); 
  5.   Class<?>[] interfaces = getAllInterfaces(type, signatureMap); 
  6.   if (interfaces.length > 0) { 
  7.    return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); 
  8.   } 
  9.   return target; 

這個方法其實是Mybatis簡化我們插件實現(xiàn)的工具方法。其實就是根據(jù)當(dāng)前攔截的對象創(chuàng)建了一個動態(tài)代理對象。代理對象的InvocationHandler處理器為新建的Plugin對象。

插件配置注解@Intercepts

Mybatis的插件都要有Intercepts注解來指定要攔截哪個對象的哪個方法。我們知道,Plugin.warp方法會返回四大接口對象的代理對象(通過new Plugin()創(chuàng)建的IvocationHandler處理器),會攔截所有的執(zhí)行方法。在代理對象執(zhí)行對應(yīng)方法的時候,會調(diào)用InvocationHandler處理器的invoke方法。Mybatis中利用了注解的方式配置指定攔截哪些方法。具體如下:

  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  2.   try { 
  3.    Set<Method> methods = signatureMap.get(method.getDeclaringClass()); 
  4.    if (methods != null && methods.contains(method)) { 
  5.      return interceptor.intercept(new Invocation(target, method, args)); 
  6.    } 
  7.    return method.invoke(target, args); 
  8.   } catch (Exception e) { 
  9.    throw ExceptionUtil.unwrapThrowable(e); 
  10.   } 

可以看到,只有通過Intercepts注解指定的方法才會執(zhí)行我們自定義插件的intercept方法。未通過Intercepts注解指定的將不會執(zhí)行我們的intercept方法。

官方插件開發(fā)方式

  1. @Intercepts({@Signature(type = Executor.class, method = "query"
  2.     args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) 
  3. public class TestInterceptor implements Interceptor { 
  4.   public Object intercept(Invocation invocation) throws Throwable { 
  5.    Object target = invocation.getTarget(); //被代理對象 
  6.    Method method = invocation.getMethod(); //代理方法 
  7.    Object[] args = invocation.getArgs(); //方法參數(shù) 
  8.    // do something ...... 方法攔截前執(zhí)行代碼塊 
  9.    Object result = invocation.proceed(); 
  10.    // do something .......方法攔截后執(zhí)行代碼塊 
  11.    return result; 
  12.   } 
  13.   public Object plugin(Object target) { 
  14.    return Plugin.wrap(target, this); 
  15.   } 

以上就是Mybatis官方推薦的插件實現(xiàn)的方法,通過Plugin對象創(chuàng)建被代理對象的動態(tài)代理對象。可以發(fā)現(xiàn),Mybatis的插件開發(fā)還是很簡單的。

自定義開發(fā)方式

Mybatis的插件開發(fā)通過內(nèi)部提供的Plugin對象可以很簡單的開發(fā)。只有理解了插件實現(xiàn)原理,對應(yīng)不采用Plugin對象我們一樣可以自己實現(xiàn)插件的開發(fā)。下面是我個人理解之后的自己實現(xiàn)的一種方式。

  1. public class TestInterceptor implements Interceptor { 
  2.   public Object intercept(Invocation invocation) throws Throwable { 
  3.     Object target = invocation.getTarget(); //被代理對象 
  4.     Method method = invocation.getMethod(); //代理方法 
  5.     Object[] args = invocation.getArgs(); //方法參數(shù) 
  6.     // do something ...... 方法攔截前執(zhí)行代碼塊 
  7.     Object result = invocation.proceed(); 
  8.     // do something .......方法攔截后執(zhí)行代碼塊 
  9.     return result; 
  10.   } 
  11.   public Object plugin(final Object target) { 
  12.     return Proxy.newProxyInstance(Interceptor.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { 
  13.       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  14.         return intercept(new Invocation(target, method, args)); 
  15.       } 
  16.     }); 
  17.   } 
  18.   public void setProperties(Properties properties) { 
  19.   } 

當(dāng)然,Mybatis插件的那這個時候Intercepts的注解起不到作用了。

小結(jié)

我們在MyBatis配置了一個插件,在運行發(fā)生了什么

  1. 所有可能被攔截的處理類都會生成一個代理
  2. 處理類代理在執(zhí)行對應(yīng)方法時,判斷要不要執(zhí)行插件中的攔截方法
  3. 執(zhí)行插接中的攔截方法后,推進(jìn)目標(biāo)的執(zhí)行

如果有N個插件,就有N個代理,每個代理都要執(zhí)行上面的邏輯。這里面的層層代理要多次生成動態(tài)代理,是比較影響性能的。雖然能指定插件攔截的位置,但這個是在執(zhí)行方法時動態(tài)判斷,初始化的時候就是簡單的把插件包裝到了所有可以攔截的地方。

因此,在編寫插件時需注意以下幾個原則:

  • 不編寫不必要的插件;
  • 實現(xiàn)plugin方法時判斷一下目標(biāo)類型,是本插件要攔截的對象才執(zhí)行Plugin.wrap方法,否者直接返回目標(biāo)本身,這樣可以減少目標(biāo)被代理的次數(shù)。
  1. // 假如我們只要攔截Executor對象,那么我們應(yīng)該這么做 
  2. public Object plugin(final Object target) { 
  3.   if (target instanceof Executor) { 
  4.    return Plugin.wrap(target, this); 
  5.   } else { 
  6.    return target; 
  7.   } 

Mybatis插件很強(qiáng)大,可以對Mybatis框架進(jìn)行很大的擴(kuò)展。當(dāng)然,如果你不理解Mybatis插件的原理,開發(fā)起來只能是模擬兩可。在實際開發(fā)過程中,我們可以參考別人寫的插件。下面是一個Mybatis分頁的插件,可以為以后開發(fā)做參考。

  1. /** 
  2.  * Mybatis - 通用分頁插件(如果開啟二級緩存需要注意) 
  3.  */ 
  4. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}), 
  5.     @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})}) 
  6. @Log4j 
  7. public class PageHelper implements Interceptor { 
  8.  
  9.   public static final ThreadLocal<Page> localPage = new ThreadLocal<Page>(); 
  10.  
  11.   /** 
  12.    * 開始分頁 
  13.    * 
  14.    * @param pageNum 
  15.    * @param pageSize 
  16.    */ 
  17.   public static void startPage(int pageNum, int pageSize) { 
  18.     localPage.set(new Page(pageNum, pageSize)); 
  19.   } 
  20.  
  21.   /** 
  22.    * 結(jié)束分頁并返回結(jié)果,該方法必須被調(diào)用,否則localPage會一直保存下去,直到下一次startPage 
  23.    * 
  24.    * @return 
  25.    */ 
  26.   public static Page endPage() { 
  27.     Page page = localPage.get(); 
  28.     localPage.remove(); 
  29.     return page; 
  30.   } 
  31.  
  32.   public Object intercept(Invocation invocation) throws Throwable { 
  33.     if (localPage.get() == null) { 
  34.       return invocation.proceed(); 
  35.     } 
  36.     if (invocation.getTarget() instanceof StatementHandler) { 
  37.       StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); 
  38.       MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler); 
  39.       // 分離代理對象鏈(由于目標(biāo)類可能被多個插件攔截,從而形成多次代理,通過下面的兩次循環(huán) 
  40.       // 可以分離出最原始的的目標(biāo)類) 
  41.       while (metaStatementHandler.hasGetter("h")) { 
  42.         Object object = metaStatementHandler.getValue("h"); 
  43.         metaStatementHandler = SystemMetaObject.forObject(object); 
  44.       } 
  45.       // 分離最后一個代理對象的目標(biāo)類 
  46.       while (metaStatementHandler.hasGetter("target")) { 
  47.         Object object = metaStatementHandler.getValue("target"); 
  48.         metaStatementHandler = SystemMetaObject.forObject(object); 
  49.       } 
  50.       MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement"); 
  51.       //分頁信息if (localPage.get() != null) { 
  52.       Page page = localPage.get(); 
  53.       BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql"); 
  54.       // 分頁參數(shù)作為參數(shù)對象parameterObject的一個屬性 
  55.       String sql = boundSql.getSql(); 
  56.       // 重寫sql 
  57.       String pageSql = buildPageSql(sql, page); 
  58.       //重寫分頁sql 
  59.       metaStatementHandler.setValue("delegate.boundSql.sql", pageSql); 
  60.       Connection connection = (Connection) invocation.getArgs()[0]; 
  61.       // 重設(shè)分頁參數(shù)里的總頁數(shù)等 
  62.       setPageParameter(sql, connection, mappedStatement, boundSql, page); 
  63.       // 將執(zhí)行權(quán)交給下一個插件 
  64.       return invocation.proceed(); 
  65.     } else if (invocation.getTarget() instanceof ResultSetHandler) { 
  66.       Object result = invocation.proceed(); 
  67.       Page page = localPage.get(); 
  68.       page.setResult((List) result); 
  69.       return result; 
  70.     } 
  71.     return null
  72.   } 
  73.  
  74.   /** 
  75.    * 只攔截這兩種類型的 
  76.    * <br>StatementHandler 
  77.    * <br>ResultSetHandler 
  78.    * 
  79.    * @param target 
  80.    * @return 
  81.    */ 
  82.   public Object plugin(Object target) { 
  83.     if (target instanceof StatementHandler || target instanceof ResultSetHandler) { 
  84.       return Plugin.wrap(target, this); 
  85.     } else { 
  86.       return target; 
  87.     } 
  88.   } 
  89.  
  90.   public void setProperties(Properties properties) { 
  91.  
  92.   } 
  93.  
  94.   /** 
  95.    * 修改原SQL為分頁SQL 
  96.    * 
  97.    * @param sql 
  98.    * @param page 
  99.    * @return 
  100.    */ 
  101.   private String buildPageSql(String sql, Page page) { 
  102.     StringBuilder pageSql = new StringBuilder(200); 
  103.     pageSql.append("select * from ("); 
  104.     pageSql.append(sql); 
  105.     pageSql.append(" ) temp limit ").append(page.getStartRow()); 
  106.     pageSql.append(" , ").append(page.getPageSize()); 
  107.     return pageSql.toString(); 
  108.   } 
  109.  
  110.   /** 
  111.    * 獲取總記錄數(shù) 
  112.    * 
  113.    * @param sql 
  114.    * @param connection 
  115.    * @param mappedStatement 
  116.    * @param boundSql 
  117.    * @param page 
  118.    */ 
  119.   private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement, 
  120.                  BoundSql boundSql, Page page) { 
  121.     // 記錄總記錄數(shù) 
  122.     String countSql = "select count(0) from (" + sql + ") temp"
  123.     PreparedStatement countStmt = null
  124.     ResultSet rs = null
  125.     try { 
  126.       countStmt = connection.prepareStatement(countSql); 
  127.       BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, 
  128.           boundSql.getParameterMappings(), boundSql.getParameterObject()); 
  129.       setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject()); 
  130.       rs = countStmt.executeQuery(); 
  131.       int totalCount = 0; 
  132.       if (rs.next()) { 
  133.         totalCount = rs.getInt(1); 
  134.       } 
  135.       page.setTotal(totalCount); 
  136.       int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1); 
  137.       page.setPages(totalPage); 
  138.     } catch (SQLException e) { 
  139.       log.error("Ignore this exception", e); 
  140.     } finally { 
  141.       try { 
  142.         rs.close(); 
  143.       } catch (SQLException e) { 
  144.         log.error("Ignore this exception", e); 
  145.       } 
  146.       try { 
  147.         countStmt.close(); 
  148.       } catch (SQLException e) { 
  149.         log.error("Ignore this exception", e); 
  150.       } 
  151.     } 
  152.   } 
  153.  
  154.   /** 
  155.    * 代入?yún)?shù)值 
  156.    * 
  157.    * @param ps 
  158.    * @param mappedStatement 
  159.    * @param boundSql 
  160.    * @param parameterObject 
  161.    * @throws SQLException 
  162.    */ 
  163.   private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, 
  164.                 Object parameterObject) throws SQLException { 
  165.     ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); 
  166.     parameterHandler.setParameters(ps); 
  167.   } 
  168.  
  169.   @Data //采用lombok插件編譯 
  170.   public static class Page<E> { 
  171.     private int pageNum; 
  172.     private int pageSize; 
  173.     private int startRow; 
  174.     private int endRow; 
  175.     private long total; 
  176.     private int pages; 
  177.     private List<E> result; 
  178.  
  179.     public Page(int pageNum, int pageSize) { 
  180.       this.pageNum = pageNum; 
  181.       this.pageSize = pageSize; 
  182.       this.startRow = pageNum > 0 ? (pageNum - 1) * pageSize : 0; 
  183.       this.endRow = pageNum * pageSize; 
  184.     } 
  185.  
  186.   } 

 

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2025-08-01 07:07:18

2024-12-27 08:39:10

2011-06-09 17:26:17

Qt 插件 API

2025-07-15 02:00:00

2009-12-11 10:29:03

PHP插件機(jī)制

2010-09-08 14:39:35

2021-06-22 06:52:46

Vite 插件機(jī)制Rollup

2025-01-02 10:10:51

2022-07-11 10:37:41

MapPart集合

2013-11-04 09:35:38

Firefox插件攔截FLASH

2024-07-17 09:23:58

Vite插件機(jī)制

2023-11-07 10:19:08

2020-12-10 08:21:27

XML映射Mybatis

2009-06-24 16:00:00

2025-07-30 01:00:25

2009-09-27 17:37:32

Hibernate攔截

2025-02-28 08:14:53

2024-05-06 00:00:00

C#工具代碼

2023-09-05 08:58:07

2024-02-28 09:35:52

點贊
收藏

51CTO技術(shù)棧公眾號

五月天av影院| 亚洲最大福利网| 欧美午夜激情影院| 国产视频一区二| 欧美色欧美亚洲高清在线视频| 日韩精品第一页| 亚洲第一天堂网| 久久综合图片| 欧美第一页在线| 日本性高潮视频| 亚洲综合影院| 欧美日韩一区二区三区四区| 无码人妻少妇伦在线电影| 成人免费在线观看| eeuss鲁片一区二区三区在线观看| 国产精品久久综合av爱欲tv| 国产亚洲欧美精品久久久www| 欧美色图激情小说| 亚洲成人免费在线视频| 日本中文字幕观看| 丝袜美腿一区| 午夜精品一区二区三区三上悠亚 | 91欧美一区二区三区| av电影一区| 亚洲图片自拍偷拍| 国产在线拍揄自揄拍无码| 黄色免费在线播放| proumb性欧美在线观看| 91日本在线观看| 亚洲国产无线乱码在线观看| 亚洲在线一区| 97精品在线观看| 久久成人在线观看| 91精品推荐| 中文字幕精品视频| 成人性生交大免费看| 欧美美女啪啪| 亚洲精品一区二区三区99| 中文字幕一区二区在线观看视频| 日韩中文视频| 在线免费一区三区| 午夜精品久久久内射近拍高清| 爱情岛论坛亚洲品质自拍视频网站| 亚洲欧美乱综合| 日韩视频在线免费播放| 免费在线观看av| 国产精品久久网站| 午夜视频久久久| 国产女主播在线写真| 国产欧美一区二区精品仙草咪| 欧美极品jizzhd欧美| 污污网站在线免费观看| av一区二区三区| 好吊色欧美一区二区三区视频| 日本毛片在线观看| 99免费精品视频| 欧美日本韩国国产| 国产一二三区在线| 欧美国产1区2区| 在线视频亚洲自拍| 伊人手机在线| 亚洲成人激情自拍| 国产乱子伦农村叉叉叉| 韩国成人漫画| 欧美色综合天天久久综合精品| 男女男精品视频站| 伊人国产精品| 精品国产sm最大网站| 成人免费无码大片a毛片| 国产精品一线天粉嫩av| 日韩在线免费av| 亚洲天堂黄色片| 在线精品观看| 国产成+人+综合+亚洲欧洲| 在线视频 91| 国产成人综合在线播放| 久久国产欧美精品| a√在线中文网新版址在线| 亚洲特级片在线| 国产妇女馒头高清泬20p多| 少妇视频一区| 欧美另类高清zo欧美| 亚洲天堂小视频| 窝窝社区一区二区| 日韩视频免费观看| 日韩精品视频播放| 秋霞成人午夜伦在线观看| 96精品久久久久中文字幕| 天天操天天干天天插| 中文字幕第一页久久| 乱熟女高潮一区二区在线| 国精一区二区三区| 在线观看视频一区| 涩视频在线观看| jvid福利在线一区二区| 久久大大胆人体| 久久久久久久久久久影院| 免费在线观看视频一区| 国产精品久久亚洲7777| 香蕉视频在线免费看| 亚洲综合一区在线| 日本特黄a级片| 欧美美女啪啪| 色综合久久天天综线观看| www毛片com| 大桥未久av一区二区三区中文| 色综合666| 麻豆免费在线| 91精品福利在线一区二区三区| 久久午夜夜伦鲁鲁片| 91精品秘密在线观看| 2020久久国产精品| 亚洲精品.www| 国产精品国产三级国产aⅴ中文 | 天天干天天操天天干天天操| 免费高潮视频95在线观看网站| 在线播放91灌醉迷j高跟美女 | 国产成人精品亚洲精品| 成人黄色免费视频| 国产精品麻豆视频| 精品久久久久久久免费人妻| 精品国产一区二区三区不卡蜜臂| 久久精品国亚洲| 日本丰满少妇做爰爽爽| 2023国产精品| 久久亚洲精品无码va白人极品| 24小时成人在线视频| 这里只有精品久久| 国产91av在线播放| www激情久久| 日日碰狠狠添天天爽超碰97| 中文字幕日韩高清在线| 蜜臀久久99精品久久久久久宅男 | 亚洲第一福利视频| 免费视频一二三区| 国产精品夜夜嗨| 日本一区二区三区四区五区六区| 亚洲伊人伊成久久人综合网| 日日噜噜噜夜夜爽亚洲精品| 中文字幕 日韩有码| 国产亚洲污的网站| www.超碰com| 清纯唯美亚洲综合一区| 国产精品com| 成人在线免费电影| 欧美日韩电影在线播放| www.av免费| 国产成人鲁色资源国产91色综| 99久久免费观看| av不卡一区| 91成人精品网站| 欧美午夜黄色| 欧美午夜影院一区| chinese全程对白| 国产九色精品成人porny| 黄色特一级视频| 国产 日韩 欧美 综合 一区| 97在线观看视频| 蜜桃视频在线播放| 欧美在线影院一区二区| 性爱在线免费视频| 国产裸体歌舞团一区二区| 日本国产中文字幕| 欧美亚洲色图校园春色| 国产va免费精品高清在线| 日韩在线资源| 日韩精品一区二区三区蜜臀| 国产一区二区三区影院| 久久久亚洲国产美女国产盗摄| 欧美婷婷精品激情| 欧美大片专区| 六月婷婷久久| 欧美v亚洲v综合v国产v仙踪林| 美女国内精品自产拍在线播放| 亚洲精品字幕在线| 91福利区一区二区三区| 麻豆明星ai换脸视频| 成人激情午夜影院| 国产又猛又黄的视频| 欧美粗暴jizz性欧美20| 美女黄毛**国产精品啪啪| 伊人久久大香线蕉综合影院首页| 久久久久久com| 成人免费视频| 欧美精品一区二区不卡| 最新国产中文字幕| 亚洲一区二区三区四区的| 黑人巨大精品欧美| 国产伦理精品不卡| 久久久久免费精品| 欧美日韩国产免费观看 | 午夜激情视频在线| 日韩精品中文字幕在线一区| 亚洲欧美综合自拍| 一区二区三区产品免费精品久久75| 中文字幕av观看| 国产麻豆精品95视频| 国产成人av影视| 亚洲小说欧美另类婷婷| 亚洲精品视频一二三| 欧美激情99| 亚洲最大的网站| 国产精品久久久久77777丨| 97国产精品视频| av网址在线| 国产亚洲欧洲在线| 香蕉国产在线视频| 日韩欧美国产精品一区| 在线播放成人av| 色悠悠久久综合| 国产亚洲精品久久久久久无几年桃| 国产精品久久久久一区二区三区| 粉嫩av懂色av蜜臀av分享| 国产精品中文字幕一区二区三区| 密臀av一区二区三区| 亚洲久久视频| 国产毛片久久久久久国产毛片| 天天射—综合中文网| 日韩精品另类天天更新| 亚洲理论电影片| 国产伦精品一区二区三区免| 久久三级中文| 成人午夜小视频| 成人一级视频| 国产精品99导航| 456亚洲精品成人影院| 午夜精品一区二区三区在线视频 | 国产精品久久av| 新版的欧美在线视频| 久久久亚洲国产| 羞羞视频在线观看不卡| 欧美成人剧情片在线观看| 黄网站在线免费| 久久久电影免费观看完整版| 午夜老司机在线观看| 日韩亚洲精品视频| 欧美性videos| 久久久av电影| 黄色av电影在线观看| 久久精品一区中文字幕| 精品孕妇一区二区三区| 久久久91精品| av在线看片| 欧美日韩999| 影音先锋中文在线视频| 欧美疯狂做受xxxx高潮| 少女频道在线观看高清| 欧美激情女人20p| 国产乱码精品一区二三赶尸艳谈| 性亚洲最疯狂xxxx高清| 黄色漫画在线免费看| 欧美亚洲激情在线| 色尼玛亚洲综合影院| 国产区精品视频| 日韩高清二区| 国产激情美女久久久久久吹潮| 成人另类视频| 欧美日韩精品免费观看| 成人羞羞网站入口| 国产av第一区| 亚洲网址在线| 国产91对白刺激露脸在线观看| 日日摸夜夜添夜夜添精品视频| 欧美特级aaa| 国产69精品一区二区亚洲孕妇| 国产情侣久久久久aⅴ免费| 97精品久久久午夜一区二区三区 | 午夜免费福利网站| 成人白浆超碰人人人人| 欧美老熟妇乱大交xxxxx | 亚洲不卡一卡2卡三卡4卡5卡精品| 欧美极品中文字幕| 黄色高清视频网站| 日韩亚洲在线| 亚洲不卡视频在线| 国产成人午夜电影网| 成人精品在线观看视频| 中文在线资源观看网站视频免费不卡 | 成人免费一级视频| 亚洲色图18p| av在线播放观看| 7m精品福利视频导航| 福利精品一区| 国产一区在线免费观看| 日韩在线观看| 和岳每晚弄的高潮嗷嗷叫视频| 日韩成人一区二区三区在线观看| www日本在线观看| 国产色91在线| 久久网免费视频| 欧美四级电影在线观看| 国模私拍视频在线| 日韩有码在线视频| 日韩伦理在线一区| 亚洲a成v人在线观看| 国产精品一在线观看| a天堂资源在线观看| 久久99精品国产.久久久久久 | 国产精品久久久久影视| 国产午夜在线播放| 欧美一区二区三区视频在线| 国产毛片在线看| 久久久视频免费观看| 婷婷久久综合九色综合99蜜桃| 精选一区二区三区四区五区| 91精品国产福利在线观看麻豆| 免费无码国产v片在线观看| 国产精品456露脸| 久久成人小视频| 在线视频观看一区| 日韩av地址| 97精品伊人久久久大香线蕉 | 日韩欧美手机在线| 亚洲毛片播放| 国产亚洲精品成人a| 亚洲人吸女人奶水| 伊人网免费视频| 亚洲新中文字幕| 精品人人视频| 国产视频精品网| 欧美午夜不卡| 男男受被啪到高潮自述| 中文字幕制服丝袜成人av | 欧美黄片一区二区三区| 欧美麻豆精品久久久久久| 在线观看二区| 国产精品99久久久久久白浆小说| 免费视频国产一区| 黄色高清无遮挡| 99久久精品一区二区| 国产在线观看免费视频今夜| 精品欧美一区二区在线观看| 2024短剧网剧在线观看| 亚洲自拍小视频| 中文字幕日韩一区二区不卡| 久久成年人网站| 日韩毛片在线免费观看| 99久久久久久久| 两个人的视频www国产精品| 欧美日韩中出| 伊人久久在线观看| 国产成人av电影| 国产亚洲精品女人久久久久久| 亚洲成色777777在线观看影院| h片在线观看下载| 精品综合在线| 日韩国产欧美一区二区三区| 日本不卡一区视频| 在线电影欧美成精品| 高清免费电影在线观看| 99re在线观看视频| 欧美涩涩视频| 国产网站无遮挡| 91久久免费观看| 欧美性天天影视| 高清一区二区三区视频| 国产日韩欧美一区| 美女100%无挡| 欧美久久高跟鞋激| 日本性爱视频在线观看| 国内精品一区二区| 日韩av在线免费观看不卡| 欧美a级片免费看| 日韩欧美国产不卡| 日韩av一卡| 中文网丁香综合网| 国产91富婆露脸刺激对白| 中文字幕亚洲乱码熟女1区2区| 中文字幕在线国产精品| 亚洲成人五区| 久久久久久久久久久福利| 国产欧美日本一区二区三区| av在线免费在线观看| 2019亚洲日韩新视频| 欧美亚洲激情| 人妻精油按摩bd高清中文字幕| 欧美日韩国产在线看| av电影在线播放高清免费观看| 成人动漫在线视频| 老**午夜毛片一区二区三区| 三级黄色片在线观看| 亚洲第一区在线观看| 91国内外精品自在线播放| 乱熟女高潮一区二区在线| 国产三级一区二区三区| 乱色精品无码一区二区国产盗| 国产精品成av人在线视午夜片| 欧美1区3d| 欧美 日韩 国产 成人 在线观看| 欧美一区二区人人喊爽| 羞羞影院欧美| 波多野结衣av一区二区全免费观看| 久久精品免视看| 乱色精品无码一区二区国产盗| 国产一区二区香蕉| 国产精品尤物| 麻豆一区产品精品蜜桃的特点| 色妞一区二区三区| 思热99re视热频这里只精品| 久久精品一二三四|