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

解讀 MyBatis 源碼:探尋數據持久化的奧秘

開發
們將逐步揭開 MyBatis 源碼那神秘的面紗,去追尋它高效數據處理、靈活映射機制以及出色性能表現背后的根源。

在當今軟件開發的廣袤領域中,MyBatis 作為一款備受青睞的持久層框架,以其強大的功能和靈活的特性發揮著重要作用。當我們深入探究 MyBatis 的源碼時,就如同開啟一扇通往技術奧秘的大門。

在這里,每一行代碼都蘊含著智慧與巧思,每一個模塊都承載著獨特的設計理念。我們將逐步揭開 MyBatis 源碼那神秘的面紗,去追尋它高效數據處理、靈活映射機制以及出色性能表現背后的根源。通過對其源碼的仔細剖析,我們不僅能更深刻地理解 MyBatis 是如何工作的,更能汲取其中的精髓,為我們自身的技術成長和項目實踐提供寶貴的經驗和啟示。讓我們懷揣著對技術的好奇與探索之心,正式踏上 MyBatis 源碼解析的精彩旅程……

詳解Mybatis的功能架構與核心技術

按照工作層次劃分可以分為三層:

  • 接口層:也就是我們用戶用到的這一層,提供各種對數據的CRUD以及配置信息維護的API調用。
  • 數據處理層:這層是框架為上層提供的關鍵,這一層實現參數映射,SQL解析,SQL執行,結果處理。
  • 基礎支撐層:負責連接管理、配置加載,事務管理、緩存機制等。

詳解Mybatis執行過程

本質上mybatis執行過程大體是:

  • 參數映射
  • sql解析
  • sql執行
  • 結果和處理映射

我們以下面這段查詢代碼為例,針對該流程進行深入講解:

Tb1Example tb1Example = new Tb1Example();
        tb1Example.createCriteria().andBirthdayIsNull();
        List<Tb1> tb1List = SpringUtil.getBean(Tb1Mapper.class).selectByExample(tb1Example);
        log.info("tb1List: {}", tb1List);

本質上我們所使用的Tb1Mapper是基于我們的xml配置動態代理生成的一個MapperProxy,在執行查詢請求時被本質上就調用這個生成代理對象,以我們的selectByExample為例,在初始配置的時候我們指明了select標簽在進行代理創建時該方法就會被標準為SELECT命令請求,執行時就會按照代理的查詢邏輯執行。

隨后代理的MapperProxy會調用MapperMethod進行參數解析,將參數轉換為后續可拼接到xml中所配置sql語句中的參數。

然后SqlSessionTemplate通過內部sqlSessionProxy的selectList著手進行實際查詢工作,其內部會拿到當前sql連接的session和xml中配置的sql還有我們上述步驟的參數生成jdbc的Statement然后通過SimpleExecutor執行sql查詢,然后通過resultSetHandler將結果解析并返回:

對此我們也給出相應的源碼,首先從MapperProxy開始調用mapperMethod進行參數解析。

//MapperProxy的invoke方法調用mapperMethod
@Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return mapperMethod.execute(sqlSession, args);
    }

//MapperMethod解析參數并基于指令匹配SQL操作
```java
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
      //......
      }
      case UPDATE: {
         //......
      }
      case DELETE: {
        //......
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          //......
        } else if (method.returnsMany()) {
        //內部進行參數解析和查詢調用
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          //......
        } else {
          //......
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
   //......
    return result;
  }

隨后步入MapperMethod進行通過convertArgsToSqlCommandParam參數解析,底層在基于sqlSession著手查詢和結果轉換:

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    //參數解析
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      //
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.selectList(command.getName(), param);
    }
   //......
    return result;
  }

最終來到SimpleExecutor的doQuery方法,通過xml配置所得的各種信息生成StatementHandler創建出Statement ,再通過resultSetHandler處理結果并返回:

@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
    //通過配置信息生成StatementHandler 
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //基于StatementHandler 生成Statement 
      stmt = prepareStatement(handler, ms.getStatementLog());
      //內部通過Statement 執行sql并將結果交由resultSetHandler轉換并返回
      return handler.query(stmt, resultHandler);
    } finally {
      //......
    }
  }

為什么Mybatis不需要實現類

是通過代理生成的,我們不妨通過源碼來看看究竟,以下面這段代碼作為入口講解原生mapper創建思路:

SqlSession sqlSession = SpringUtil.getBean(SqlSessionFactory.class).openSession();
 Tb1Mapper mapper = sqlSession.getMapper(Tb1Mapper.class);

本質上getMapper會基于接口和sqlSession信息通過mapper創建工廠mapperProxyFactory ,然后mapperProxyFactory 底層通過反射的方式創建JDK動態代理mapper對象:

對此我們給出getMapper的入口,邏輯和筆者說的一樣mapperProxyFactory 傳遞元信息進行動態代理創建:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
   //......
   
    try {
    //通過mapperProxyFactory創建動態代理
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

查看newInstance方法即可看到我們所說的基于類加載器、接口信息和methodCache內部的MapperMethodInvoker完成動態代理對象的創建:

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

如何實現Mybatis插件

Mybatis支持對ParameterHandler、ResultSetHandler、StatementHandler、Executor進行攔截,例如我們想對mybatis查詢的SQL結果解析階段進行攔截,我們可以編寫下面這樣一段代碼:

import java.sql.Statement;

//@Intercepts({@Signature(
//        type = Executor.class,  //確定要攔截的對象
//        method = "query",        //確定要攔截的方法
//        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}   //攔截方法的參數
//)})

@Intercepts({@Signature(
        type = ResultSetHandler.class,  //確定要攔截的對象
        method = "handleResultSets",        //確定要攔截的方法
        args = {Statement.class}   //攔截方法的參數
)})
public class MyInterceptor implements Interceptor {


    private static Logger logger = LoggerFactory.getLogger(MyInterceptor.class);


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        logger.info("請求被攔截,攔截類:[{}],請求方法:[{}]請求參數[{}]", invocation.getTarget().getClass().getName(),
                invocation.getMethod().getName(),
                invocation.getArgs());
        //如果當前代理的是一個非代理對象,那么就會調用真實攔截對象的方法
        // 如果不是它就會調用下個插件代理對象的invoke方法
        Object obj = invocation.proceed();
        logger.info("請求被攔截結果:[{}]", obj);
        return obj;
    }
}

然后配置文件,增加對這個攔截類的配置:

<plugins>
        <plugin interceptor="com.sharkchili.mapper.MyInterceptor">
            <property name="dbType"  value="mysql"/>
        </plugin>
    </plugins>

執行我們的請求:

// 可以從配置或者直接編碼來創建SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2)通過SqlSessionFactory創建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3)通過sqlsession執行數據庫操作
        User1Mapper user1Mapper = sqlSession.getMapper(User1Mapper.class);
        User1 user = user1Mapper.select("1");

        logger.info("查詢結果:[{}]", user.toString());

        if (sqlSession != null) {
            sqlSession.close();
        }

從輸出結果就可以看出,我們的方法攔截到了結果處理的邏輯了。

2022-11-30 10:11:55,389 [main] DEBUG [com.sharkchili.mapper.User1Mapper.select] - ==>  Preparing: select * from user1 where id = ?
2022-11-30 10:11:55,526 [main] DEBUG [com.sharkchili.mapper.User1Mapper.select] - ==> Parameters: 1(String)
[main] INFO com.sharkchili.mapper.MyInterceptor - 請求被攔截,攔截類:[org.apache.ibatis.executor.resultset.DefaultResultSetHandler],請求方法:[handleResultSets]請求參數[[org.apache.ibatis.logging.jdbc.PreparedStatementLogger@12d2ce03]]
2022-11-30 10:12:06,928 [main] DEBUG [com.sharkchili.mapper.User1Mapper.select] - <==      Total: 1
[main] INFO com.sharkchili.mapper.MyInterceptor - 請求被攔截結果:[[User1{id='1', name='小明', user2=null}]]
[main] INFO com.sharkchili.mapper.MyBatisTest - 查詢結果:[User1{id='1', name='小明', user2=null}]
2022-11-30 10:12:06,938 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@24959ca4]

Mybatis插件的工作原理

Mybatis如何引入自定義插件?

我們的業務代碼如下,創建SqlSessionFactory:

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

步入build邏輯會看到xml解析相關代碼有一行,如下所示,可以看出他就是對xml文件中plugins標簽進行解析:

//獲取標簽內容并反射生成攔截器存到某個list中
this.pluginElement(root.evalNode("plugins"));

其內部做的就是解析xml配置,生成攔截器對象MyInterceptor,并存放到interceptorChain中的一個名為interceptors的list中。

private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(var2.hasNext()) {
            //解析配置生成Interceptor 
                XNode child = (XNode)var2.next();
                String interceptor = child.getStringAttribute("interceptor");
                Properties properties = child.getChildrenAsProperties();
                Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).getDeclaredConstructor().newInstance();
                interceptorInstance.setProperties(properties);
                //存到攔截器鏈中
                this.configuration.addInterceptor(interceptorInstance);
            }
        }

    }

我們看看addInterceptor,邏輯非常簡單,說白了就是存到一個名為interceptors的list集合中,然后進行鏈式調用:

public void addInterceptor(Interceptor interceptor) {
 
        this.interceptorChain.addInterceptor(interceptor);
    }

執行真正邏輯,調用插件:

//3)通過sqlsession執行數據庫操作
        User1Mapper user1Mapper = sqlSession.getMapper(User1Mapper.class);
        User1 user = user1Mapper.select("1");

注意在Plugin的signatureMap插個斷點,如下所示:

這時候進行debug,我們可以看到堆棧中停在這樣一段代碼上。由于我們編寫了一個結果解析的攔截插件MyInterceptor,所以在newResultSetHandler時會從上文注冊的interceptorChain中取出對應處理器,給我們的resultSetHandler ,這過程中piugin類會通過Plugin.wrap(target, this);對我們的結果處理類進行包裝。

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
//獲取我們的結果解析器
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        //調用pluginAll將所有插件都引入
        ResultSetHandler resultSetHandler = (ResultSetHandler)this.interceptorChain.pluginAll(resultSetHandler);
        return resultSetHandler;
    }

所以我們步入看看wrap的邏輯,可以看到interceptor即我們自己編寫的插件,他會通過getSignatureMap獲取這個我們編寫插件MyInterceptor注解上的信息,通過反射生成一個新的代理對象,這個對象存放著signatureMap。

public static Object wrap(Object target, Interceptor interceptor) {
//獲取自定義插件信息存到signatureMap 中
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        // 使用插件包裝我們的目標類
        return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
    }

執行SQL邏輯:

最終我們的結果處理插件會在handleResultSets階段發現signatureMap里面有值,當前處理器有攔截,執行this.interceptor.intercept(new Invocation(this.target, method, args))攔截相關處理器執行我們的MyInterceptor邏輯。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
        //發現signatureMap有值
            Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
            //methods 中包含我們的這個階段的方法handleResultSets,故調用this.interceptor.intercept(new Invocation(this.target, method, args))
            return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
        } catch (Exception var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }

Mybatis的分頁插件的實現原理

通過上面工作原理的介紹我們就知道原理了,分頁插件就是通過mybatis提供的接口,攔截Executor的query方法,重寫執行的sql,例如select * from student,攔截sql后重寫為:select t.* from (select * from student) t limit 0, 10;即可。

責任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關推薦

2010-02-03 16:15:05

Python語言

2010-01-15 10:22:24

C++語言

2011-07-07 09:12:46

智慧運算WatsonPower

2023-10-27 13:59:30

Mybatis占位符

2010-02-04 11:06:14

2020-08-26 10:25:16

智慧

2010-12-01 14:37:00

2023-10-08 08:22:33

2021-03-18 08:18:15

ZooKeeper數據持久化

2022-03-18 15:55:15

鴻蒙操作系統架構

2018-12-14 09:48:23

Redis數據故障

2021-01-21 08:49:52

數據單體架構

2023-08-17 16:17:00

Docker前端

2021-07-26 05:33:59

自動化領導CIO

2017-09-21 08:16:33

數據存儲環境

2017-08-17 09:46:42

大數據諸葛io數據挖掘

2023-09-08 08:42:01

數據場景項目

2021-09-01 07:21:40

ArrayPool源碼Bucket

2011-07-10 16:04:01

程序員
點贊
收藏

51CTO技術棧公眾號

不卡视频一区二区| www.午夜精品| 午夜免费福利在线| 免费黄色网址在线观看| 国产精品性做久久久久久| 久久久视频精品| 国产精品成人无码免费| 欧美另类中文字幕| 色综合天天视频在线观看| 一区二区三区的久久的视频| 人人妻人人玩人人澡人人爽| 蜜桃免费网站一区二区三区| 久久久久久网站| 一本色道久久88| 偷窥自拍亚洲色图精选| 这里只有精品99re| 动漫av免费观看| 丰满的护士2在线观看高清| 日本一区二区综合亚洲| 国产色综合一区二区三区| 亚洲不卡在线视频| 极品裸体白嫩激情啪啪国产精品| 中文字幕日韩在线播放| 91视频啊啊啊| 一区中文字幕| 制服丝袜亚洲色图| 欧美日韩亚洲自拍| 高清在线视频不卡| 亚洲激情图片qvod| 亚洲国产午夜伦理片大全在线观看网站 | 欧美少妇另类| 成人黄色网址在线观看| 成人在线小视频| 在线观看国产黄| 亚洲在线黄色| 91a在线视频| 国产一级二级三级| 午夜精品久久久久99热蜜桃导演 | 免费久久99精品国产自| 性一交一乱一透一a级| 久久爱www久久做| 国产精品久久99久久| av大片免费观看| 亚洲一级影院| 欧美极品第一页| 午夜写真片福利电影网| 999精品一区| 色yeye香蕉凹凸一区二区av| 精品人妻无码一区二区三区换脸| 欧美变态网站| 亚洲精品720p| 国产精品久久无码| 欧洲vs亚洲vs国产| 亚洲国产成人精品一区二区| 国产探花一区二区三区| 久久的色偷偷| 精品国偷自产国产一区| 国产精品无码自拍| 国产精品传媒| 亚洲剧情一区二区| 亚洲精品视频网址| 99成人在线视频| 理论片在线不卡免费观看| 欧美卡一卡二卡三| 亚洲经典三级| 欧美在线视频一二三| 亚洲综合图片网| 毛片一区二区三区| 成人在线精品视频| 亚洲乱色熟女一区二区三区| 风间由美性色一区二区三区| 国产三区二区一区久久| 黄色毛片在线观看| 中文字幕人成不卡一区| 草草草视频在线观看| 丁香花在线高清完整版视频| 午夜精品一区二区三区三上悠亚| 狠狠97人人婷婷五月| 人人鲁人人莫人人爱精品| 欧美视频精品在线观看| 自拍视频第一页| 亚洲精品蜜桃乱晃| 精品国产区一区二区三区在线观看 | 亚洲中文字幕在线观看| 国产一区二区精品久久| 国产一区二区视频在线免费观看| 精品美女视频在线观看免费软件 | 色网站在线播放| 三级亚洲高清视频| 国产原创欧美精品| 亚洲精品.www| 九九视频免费看| 亚洲免费婷婷| 91亚洲国产成人精品性色| 亚洲免费视频网| 国产精品日日摸夜夜摸av| 精品人妻大屁股白浆无码| 蜜桃麻豆影像在线观看| 欧美精品一二三区| 中文字幕在线播放一区| 欧美第十八页| 欧美亚洲另类激情另类| 国产又黄又大又爽| 26uuu国产电影一区二区| 手机成人av在线| 偷拍视频一区二区三区| 欧美成人乱码一区二区三区| 一级肉体全黄裸片| 亚洲欧洲一区| 91亚洲va在线va天堂va国 | 精品视频一区二区三区在线观看 | 91嫩草视频在线观看| 黄色电影免费在线看| 亚洲国产精品精华液网站| 免费一区二区三区在线观看| 欧美a一欧美| 久久99久久99精品中文字幕| 免费黄色片视频| 成人ar影院免费观看视频| 国产对白在线播放| 韩国成人在线| 亚洲男人天堂九九视频| 国产在线观看免费av| 蜜桃精品视频在线| 欧美日韩一区二区三区在线视频| 婷婷色在线资源| 3atv一区二区三区| 99热99这里只有精品| 噜噜噜躁狠狠躁狠狠精品视频| 成人看片视频| 性欧美高清come| 91精品免费在线观看| 日本爱爱爱视频| 久久影院亚洲| 久久人人97超碰人人澡爱香蕉| 欧美人与性动交α欧美精品济南到| 欧美日韩在线观看一区二区| 亚洲精品午夜视频| 久久精品九九| 欧美一区二视频在线免费观看| 爱看av在线入口| 精品久久久网站| 欧美日韩在线国产| 国产一区二区三区在线观看免费| 一区二区三区四区国产| 美女视频一区| 日韩在线免费视频观看| 伊人免费在线观看高清版| 国产亚洲午夜高清国产拍精品| 91视频最新入口| 日韩啪啪网站| 日韩美女视频免费在线观看| 免费一级在线观看播放网址| 欧美性xxxxx极品| 精品国产av无码| 日韩精品成人一区二区三区| 日韩一区国产在线观看| 欧美亚洲黄色| 美日韩精品视频免费看| 丰满肥臀噗嗤啊x99av| 亚洲地区一二三色| a级在线观看视频| 美女精品在线观看| 亚洲精品中字| 日韩av综合| 性欧美在线看片a免费观看| 天堂a√中文在线| 欧美影院精品一区| 午夜国产福利一区二区| 国产福利一区在线| av免费观看网| 99久久精品国产亚洲精品| 亚洲综合日韩在线| 黄毛片在线观看| 国产一区二区三区在线视频| 国产裸体永久免费无遮挡| 亚洲国产日韩一级| 亚洲天堂视频一区| 青青青爽久久午夜综合久久午夜| 日韩中文一区| 国产一区二区三区视频在线| 国模精品系列视频| 久草在线网址| 欧美一级黄色片| 国产一级做a爱片久久毛片a| 国产精品看片你懂得| 97免费公开视频| 久久午夜激情| 亚洲中文字幕无码一区二区三区| 亚洲aaa级| 91久久精品一区二区别| www.精品| 久99久在线视频| 国产福利在线| 亚洲国产欧美一区| 国产毛片毛片毛片毛片| 欧美日韩一区二区三区| 精品人妻伦九区久久aaa片| 不卡一区中文字幕| 三级一区二区三区| 亚洲一区二区三区免费在线观看| 中文字幕精品一区日韩| 欧美自拍视频| 99re在线国产| 欧美激情福利| 国产成人精品999| 波多野结衣中文在线| 日韩一区二区久久久| 青青草视频在线免费观看| 欧美不卡一区二区三区四区| 国语对白做受69按摩| 亚洲高清三级视频| 中文字幕无码日韩专区免费 | 日韩福利电影在线| 成人一对一视频| 欧美1区2区| 亚洲精品高清视频| 亚洲色图美女| 国严精品久久久久久亚洲影视| 国产精品日本一区二区不卡视频 | 亚洲美女屁股眼交| 日韩欧美视频免费观看| 91麻豆文化传媒在线观看| 成人一区二区三区仙踪林| 九九久久精品视频 | 国产精品自在在线| 欧美 日韩 国产 激情| 在线一区视频| 久久亚洲中文字幕无码| 国内自拍视频一区二区三区 | 欧洲在线视频| 久久亚洲精品一区二区| 在线视频三区| 影音先锋日韩有码| 国产乱视频在线观看| 日韩激情在线视频| 亚洲av成人精品毛片| 欧美不卡一二三| 国产高中女学生第一次| 欧美一区二区三区视频在线| 国产毛片在线视频| 欧美一区二区观看视频| 国产视频第二页| 日韩欧美一区在线| 国产情侣自拍小视频| 91精品国产黑色紧身裤美女| 99久久久国产精品无码免费| 91麻豆精品91久久久久同性| 国产日产亚洲系列最新| 日韩午夜激情av| 蜜臀久久99精品久久久| 亚洲精品456在线播放狼人| 天天干天天爽天天操| 日韩成人在线观看| 久青青在线观看视频国产| 国产亚洲精品va在线观看| 成年人在线视频免费观看| 中文字幕欧美精品在线 | 性色av一区二区三区免费| a毛片不卡免费看片| 91po在线观看91精品国产性色| 中文字幕成在线观看| 国产精品久久久久77777| 激情小说亚洲| 亚洲一区二区三区四区视频 | 韩国女主播成人在线观看| 久久久福利影院| www.视频一区| 美国黑人一级大黄| 亚洲欧美成aⅴ人在线观看| 国产 日韩 欧美 成人| 日韩欧美在线免费| 中文字幕永久免费视频| 91精品国产色综合久久ai换脸 | fc2成人免费人成在线观看播放 | 亚洲欧美日韩小说| 日韩精品一区二区三区国语自制| 色婷婷亚洲综合| 999久久久久久| 精品电影一区二区三区| 青青草视频在线免费观看| yellow中文字幕久久| www.51av欧美视频| 国产精品午夜一区二区欲梦| 国产成人在线中文字幕| 人禽交欧美网站免费| 欧美欧美全黄| 国产精品亚洲αv天堂无码| 精品一区二区三区在线观看| 亚洲熟女乱综合一区二区三区| 国产精品美女久久久久久久久| 欧美日韩在线观看成人| 在线精品视频一区二区三四| www.综合色| 在线视频欧美性高潮| a√中文在线观看| 成人福利视频网| 免费成人结看片| 亚洲精品天堂成人片av在线播放| 日精品一区二区| 亚洲精品激情视频| 国产精品久久免费看| 欧美日韩综合在线观看| 欧美一区二区观看视频| av在线之家电影网站| 97久久久久久| 欧美日韩黄网站| 亚洲人成77777| 欧美亚洲一区二区三区| 日本50路肥熟bbw| 亚洲天堂免费看| 亚洲婷婷久久综合| 日韩国产欧美精品在线| 精品精品导航| 亚洲va电影大全| 日本成人小视频| 玩弄japan白嫩少妇hd| av一区二区三区四区| 久久久久久久久久99| 在线观看91av| 在线观看国产原创自拍视频| 日本精品久久电影| 九九热hot精品视频在线播放| 成人免费看片视频在线观看| 美女www一区二区| 欧美黄色一级生活片| 欧美午夜片在线免费观看| 日本成人动漫在线观看| 欧美极品美女视频网站在线观看免费 | 日本一区高清| 国产91精品久| 精品综合久久88少妇激情| r级无码视频在线观看| 成人午夜免费视频| 欧美一级高潮片| 欧美成人性战久久| gogogogo高清视频在线| 成人综合国产精品| 91成人观看| 波多野结衣三级视频| 亚洲一级在线观看| 亚洲精品18在线观看| 久久久综合av| 美女主播精品视频一二三四| 欧美变态另类刺激| 96av麻豆蜜桃一区二区| 亚洲天堂av片| 亚洲人午夜精品免费| 丁香久久综合| 午夜啪啪福利视频| 国产成人精品免费视频网站| 久久久一二三区| 亚洲精品电影在线| 一区二区电影免费观看| 日韩久久精品一区二区三区| 日本最新不卡在线| 亚洲女人久久久| 日韩欧美国产一区二区三区| 秋霞在线午夜| 久久www免费人成精品| 久久婷婷av| 美女三级黄色片| 日韩欧美国产成人一区二区| 国产91足控脚交在线观看| 久久久精品动漫| 日本欧美久久久久免费播放网| 久艹在线观看视频| 精品卡一卡二卡三卡四在线| 伊人色综合一区二区三区影院视频| 日韩一区二区三区高清| 黄页视频在线91| 成人免费看片98| 亚洲人成人99网站| 看亚洲a级一级毛片| www.av中文字幕| 国产精品亲子乱子伦xxxx裸| 精品久久久久中文慕人妻| 91精品国产91久久| 日韩欧美1区| 欧美国产日韩在线视频| 福利微拍一区二区| 欧美激情午夜| 精品一区二区久久久久久久网站| 日本一区中文字幕| 久青草免费视频| 一区二区三欧美| 一区二区在线免费播放| 激情综合网俺也去| 亚洲一本大道在线| 在线观看国产原创自拍视频| 国产伦精品一区二区三区在线| 日韩精品免费专区| 久久婷婷国产麻豆91| 在线播放国产一区中文字幕剧情欧美 | 7777精品伊人久久久大香线蕉的| 国产夫妻在线| 丰满女人性猛交| 久久精品免费在线观看| 粉嫩av一区二区夜夜嗨| 成人国产精品av|