精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

国产日韩欧美一区二区三区在线观看| 成人性生交大片免费看中文视频 | 亚洲狠狠婷婷| 亚洲色图美腿丝袜| 国产精品探花在线播放| 国产夫妻在线播放| 国产精品国产三级国产aⅴ原创| 91在线免费视频| 特黄视频免费看| 99视频精品全国免费| 亚洲福利影片在线| 日日干日日操日日射| 91九色在线看| 亚洲欧美日韩久久精品| 欧美精品欧美精品系列c| 国产又粗又大又爽| 久久精品午夜| 久久久久久久久久久亚洲| 一级黄色片网址| 国产精品15p| 7777精品伊人久久久大香线蕉的| 欧美亚洲另类色图| 暧暧视频在线免费观看| 国产精品久久看| 蜜桃狠狠色伊人亚洲综合网站| 国产精品伦理一区| 欧美aaa在线| 日本欧美一二三区| 日本中文字幕免费| 黄色欧美日韩| 欧美成人国产va精品日本一级| 亚洲第一视频区| 亚洲人和日本人hd| 日韩av影片在线观看| 91成人在线观看喷潮蘑菇| 本网站久久精品| 福利一区福利二区微拍刺激| 欧洲金发美女大战黑人| 国产69久久| 久久精品一区二区三区四区| 国产精品一区二区三区免费| a级片在线播放| 国内精品免费**视频| 国产精品色午夜在线观看| 日韩在线播放中文字幕| 国产精品资源| 国产97免费视| 色av性av丰满av| 久久亚洲综合| 国产成人av在线| 欧美一级黄视频| 日韩av中文字幕一区二区三区| 国产成人高潮免费观看精品| 青青青国产在线| 久久亚洲精品伦理| 国产精品视频精品| 国产精品久久久午夜夜伦鲁鲁| 精品一区二区在线播放| 成人乱人伦精品视频在线观看| 亚洲一卡二卡在线观看| 精品影视av免费| 成人动漫视频在线观看免费| 亚洲av无码一区二区乱子伦| 国产99久久久精品| 精品欧美一区二区久久久伦| 午夜黄色小视频| 99精品在线观看视频| 欧美12av| 日本韩国在线视频爽| 亚洲欧美视频一区| 日韩小视频在线播放| 樱桃视频成人在线观看| 欧美性猛交xxxx乱大交退制版| 99sesese| 操欧美女人视频| 国产视频精品一区二区三区| www.狠狠爱| 国产精品精品| 亚州国产精品久久久| 五月天激情四射| 精品一区二区三区av| 成人激情直播| 免费av在线电影| 亚洲视频在线一区观看| 国产成人在线小视频| 英国三级经典在线观看| 欧美日韩国产精选| 在线xxxxx| 欧美一区电影| 欧美国产精品日韩| 日本一本在线观看| 国产成人精品一区二区三区四区| 精品亚洲一区二区三区四区五区高| 国产系列在线观看| 一区二区三区av电影 | 免费观看日韩av| 成人在线免费网站| www.国产精品.com| 亚洲午夜在线电影| 在线观看国产中文字幕| 久久久久高潮毛片免费全部播放| 国产一区二区三区在线免费观看| 久久精品99国产精| 蜜桃视频在线一区| 久久综合中文色婷婷| 成人黄色网址| 欧美影视一区在线| 亚洲国产精品自拍视频| 婷婷成人基地| 国产精品第8页| 亚欧在线观看视频| 亚洲精品第1页| www.com黄色片| 丝袜美腿综合| 欧美肥老妇视频| 91精品国产色综合久久不8| 91浏览器在线视频| 日本福利视频一区| 精品国产亚洲一区二区在线观看| 国产一区二区日韩| 国产精品国产三级国产专区52| 国产高清在线观看免费不卡| 涩涩涩999| 激情开心成人网| 亚洲国产精品中文| www.av视频在线观看| 精品一区二区影视| 亚洲一区二区免费视频软件合集| www.日韩| 亚洲美女动态图120秒| 国产一级视频在线| 国产91在线|亚洲| 日韩视频一二三| 激情综合五月| 日韩亚洲欧美中文高清在线| 69视频免费看| 国产色产综合产在线视频| 色综合av综合无码综合网站| 国产精品色在线网站| 欧美激情18p| 亚洲欧美黄色片| 亚洲午夜精品网| 亚洲精品第二页| 日韩午夜在线电影| 精品国产乱码久久久久久郑州公司 | 欧美日韩在线一| 粉嫩一区二区三区四区公司1| 欧美美女15p| 精品人妻久久久久一区二区三区| 亚洲欧美日韩国产中文在线| 先锋资源在线视频| 欧美日本一区二区视频在线观看| 91欧美激情另类亚洲| 超碰免费在线播放| 日韩免费看网站| 精品少妇久久久久久888优播| 国产激情精品久久久第一区二区| 国产精品免费看久久久无码| 99re热精品视频| 97成人超碰免| 国产青青草在线| 欧美精品色一区二区三区| 国产精品免费人成网站酒店| 韩国一区二区三区| 欧美图片激情小说| 亚洲区小说区| 成人天堂噜噜噜| 欧美黄色视屏| 精品亚洲一区二区三区四区五区| 蜜臀尤物一区二区三区直播| 日本一区二区成人在线| 欧美性猛交xxxx乱大交91| 欧美日韩免费| 欧美大香线蕉线伊人久久国产精品| 欧美片第一页| 久久精品视频网站| 手机av免费在线观看| 色成人在线视频| 可以直接看的黄色网址| fc2成人免费人成在线观看播放| 成年人免费大片| 66视频精品| 久久爱av电影| 日韩三级成人| 97超视频免费观看| 成视频免费观看在线看| 亚洲韩国日本中文字幕| 亚洲熟女乱色一区二区三区久久久 | 久久99久久久久久久久久久| 亚洲精品少妇一区二区| 久9久9色综合| 99理论电影网| 日韩精品免费观看视频| 欧美日韩国产成人在线| 精品影院一区| 精品美女在线播放| 在线观看国产一区二区三区| 亚洲一区二区视频在线观看| 一级特黄曰皮片视频| 成人国产精品免费| 亚洲天堂2018av| 国产欧美在线| 国产在线拍揄自揄拍无码| 欧美日韩精品一区二区三区在线观看| 国产精品香蕉在线观看| 川上优av中文字幕一区二区| 精品国产依人香蕉在线精品| 欧美日韩国产综合视频| 欧美成人乱码一区二区三区| 中文字幕乱码一区二区| 欧美日韩国产页| 九九久久免费视频| 综合久久给合久久狠狠狠97色| 可以直接看的无码av| 东方aⅴ免费观看久久av| 九九热免费在线观看| 天堂蜜桃一区二区三区 | 日韩电影免费在线看| 成人免费在线视频播放| 国产精品久久天天影视| 日韩.欧美.亚洲| 性欧美xxxx免费岛国不卡电影| av免费观看久久| av成人在线网站| 国产精品一久久香蕉国产线看观看| 在线天堂资源www在线污| 欧美激情videos| 美女尤物在线视频| 欧美成人在线免费视频| 成人福利网站| 精品国偷自产在线| 9191在线| 日韩在线观看你懂的| 成人高清免费观看mv| 亚洲男人第一av网站| 婷婷在线观看视频| 亚洲成av人乱码色午夜| 丰满人妻一区二区| 精品日韩在线一区| 亚洲精品久久久久久久久久| 日韩一区二区在线看片| 99久久精品无免国产免费| 欧美精品久久99| 91黄色在线视频| 91精品国产综合久久福利| 国产一区二区在线视频聊天| 91精品国产综合久久小美女| 99热这里只有精品在线| 日韩一区二区三区精品视频| 国产高清在线观看视频| 欧美tk丨vk视频| 五月婷中文字幕| 亚洲男女性事视频| 国产免费av在线| 中文字幕一区二区三区电影| 北条麻妃在线| 久久精品人人爽| av在线理伦电影| 欧美性在线观看| 日本成人伦理电影| 成人精品久久一区二区三区| 国产aa精品| 国产伦精品一区二区三区高清版 | 深夜视频一区二区| 91精品美女在线| 都市激情亚洲| 欧美日韩另类综合| 日韩欧美综合| 麻豆映画在线观看| 亚洲专区一区| www.99r| 成年人国产精品| 亚洲无人区码一码二码三码的含义| 日本一区二区三区免费乱视频 | 婷婷久久综合九色国产成人| 依依成人综合网| 欧美群妇大交群中文字幕| www.色日本| 亚洲欧美国产另类| 国产淫片在线观看| 7m精品福利视频导航| 免费一区二区三区四区| 国产精品成人一区二区三区| 欧美美女在线| 亚洲色图都市激情| 日韩精品欧美精品| 亚洲熟女乱综合一区二区| 久久综合久久综合九色| 国产传媒免费在线观看| 狠狠操狠狠色综合网| 亚洲一卡二卡在线观看| 亚洲精品久久久久中文字幕欢迎你| av资源网站在线观看| 欧美极品美女电影一区| 88xx成人网| 精品免费二区三区三区高中清不卡| 91欧美日韩| 91传媒久久久| 福利91精品一区二区三区| 久久久久无码精品国产sm果冻| 亚洲国产一区视频| 曰批又黄又爽免费视频| 日韩成人黄色av| 在线三级中文| 国产欧美日韩精品专区| 任你躁在线精品免费| 日韩a级黄色片| 精品在线播放免费| 国产又黄又粗视频| 精品日韩美女的视频高清| 国产精品亚洲欧美在线播放| 亚洲色图50p| 国产在线观看www| 成人欧美一区二区三区在线 | 成年人深夜视频| 久久99精品久久久久久国产越南| 欧美老熟妇乱大交xxxxx| 亚洲影院免费观看| 国产麻豆精品一区| 中文字幕视频一区二区在线有码 | 欧美日韩a区| 国产一级免费大片| 国产精品欧美一区二区三区| 亚洲黄色激情视频| 亚洲激情视频网| 国产精品探花在线| 97超碰人人模人人爽人人看| 久久美女精品| www亚洲成人| 国产亚洲一区字幕| √资源天堂中文在线| 亚洲国产精品yw在线观看| 欧洲性视频在线播放| 亚洲a中文字幕| 一区二区中文| 波多野结衣中文字幕在线播放| 欧美高清在线一区二区| 成人午夜精品视频| 国产一区二区精品丝袜| 国产精品久久亚洲不卡| 日韩国产精品一区二区三区| 先锋亚洲精品| 婷婷色一区二区三区| 在线观看视频一区二区欧美日韩| 国产三级在线| 国产精品高清在线观看| 青青草原综合久久大伊人精品 | 色悠久久久久综合先锋影音下载| 最新av在线免费观看| 国产寡妇亲子伦一区二区| 久久亚洲精品大全| 亚洲黄在线观看| 久久久久久久| 亚洲精品国产精品国自产观看| 日韩av在线发布| 精品一区二区在线观看视频| 91精品国产综合久久香蕉麻豆| 欧美bbbxxxxx| 精品一区久久| 水野朝阳av一区二区三区| 国精产品一区一区| 欧美一区二区三区在线视频 | 日韩美女主播视频| 成人网18免费网站| 国产一级片中文字幕| 亚洲国产wwwccc36天堂| 三级在线视频| 国产美女精品免费电影| 亚洲午夜精品一区二区国产 | 国产图片一区| 成人精品视频一区二区| 国产精品久久久久9999吃药| 国产女18毛片多18精品| 午夜精品免费视频| sdde在线播放一区二区| 91香蕉视频免费看| 天天操天天干天天综合网| 国产日韩精品在线看| 亚洲va电影大全| 午夜在线视频观看日韩17c| 超薄肉色丝袜一二三| 日韩欧美国产午夜精品| 午夜影视一区二区三区| 亚洲欧洲一区二区福利| 成人性生交大片免费看视频在线| 无码人妻黑人中文字幕| 久久五月天色综合| 香蕉久久精品| 香蕉视频xxx| 色综合色狠狠天天综合色| 国产丝袜在线| 欧美日韩一区二区视频在线观看| 国产在线视频精品一区| 99久热在线精品996热是什么| 色偷偷av一区二区三区| 日韩mv欧美mv国产网站| 日韩av福利在线观看| 91久久精品午夜一区二区| 美女精品视频| 中文字幕人成一区|