亚洲欧美综合网_国产丶欧美丶日本不卡视频_久久精品亚洲精品国产欧美kt∨ _国产精品自在欧美一区_欧美成人精品激情在线观看 _婷婷丁香久久五月婷婷_国产一本一道久久香蕉_国产www精品_亚洲国产精品久久久久秋霞蜜臀_国产精品xxxxx

世界訊息:【Spring源碼】- 08 擴展點之mybatis集成

2023-03-29 11:15:51 來源:騰訊云

概述

mybatis將與spring集成的代碼拆分到了mybatis-spring模塊,避免mybatisspring之間的耦合,如果你只需要純粹的使用mybatis api,就避免了必須將spring依賴也耦合進來的問題。mybatis使用中一般是將Sql語句寫在xml文件中,為方便操作,我們會創建一個Mapper接口文件進行映射,mybatis提供了采用動態代理方式對Mapper接口類進行包裝,這樣我們就可以像使用普通對象一樣執行各種方法調用。

mybatisspring集成的一個核心任務就是將這些動態代理包裝的Mapper對象注入到IoC容器中,這樣其它Bean就可以方便的使用如@Autowired等方式進行依賴注入。


【資料圖】

MapperScannerConfigurer

需要將mybatis生成的動態代理對象注入到IoC容器中,自然我們想到之前的BeanFactoryPostProcessor的子類BeanDefinitionRegistryPostProcessor這個擴展類。MapperScannerConfigurer就是實現了BeanDefinitionRegistryPostProcessor接口,然后在該接口中通過類掃描器scanner進行掃描注冊。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) {     processPropertyPlaceHolders();    }    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);    scanner.setAddToConfig(this.addToConfig);    scanner.setAnnotationClass(this.annotationClass);    scanner.setMarkerInterface(this.markerInterface);    scanner.setSqlSessionFactory(this.sqlSessionFactory);    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);//指定引用的SqlSessionFactory    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);    scanner.setResourceLoader(this.applicationContext);    scanner.setBeanNameGenerator(this.nameGenerator);    scanner.registerFilters();    //basePackage指定掃描Mapper接口包路徑    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}

ClassPathMapperScanner這個就是繼承之前介紹過的SpringClassPathBeanDefinitionScanner類掃描器進行了擴展,它可以實現將包路徑下至少含有一個方法的接口類注冊到IoC中。

這里有個問題:注冊進入的BeanDefinitionbeanClass指向的都是接口,到后續創建對象時會存在問題,接口是沒法創建實例的。所以,ClassPathMapperScanner掃描器在注冊完成后,又會對BeanDefinition進行處理。處理邏輯位于ClassPathMapperScanner#processBeanDefinitions()方法中,其核心邏輯見下:

private void processBeanDefinitions(Set beanDefinitions) {    GenericBeanDefinition definition;    for (BeanDefinitionHolder holder : beanDefinitions) {      definition = (GenericBeanDefinition) holder.getBeanDefinition();      String beanClassName = definition.getBeanClassName();      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59      definition.setBeanClass(this.mapperFactoryBeanClass);   ...    }}

其中最重要的一條語句:definition.setBeanClass(this.mapperFactoryBeanClass),偷偷的將BeanDefinitionbeanClass替換成了MapperFactoryBean,而不再指向Mapper接口類。同時將Mapper接口類作為參數傳入到了MapperFactoryBean中,即調用下面構造方法:

public MapperFactoryBean(Class mapperInterface) {    this.mapperInterface = mapperInterface;}

MapperFactoryBean實現了FactoryBean接口,這樣實際上它是通過getObject()方法獲取到對象然后注入到IoC容器中。而在getObject()方法中,我們就可以使用mybatis api獲取到Mapper接口類的動態代理對象:SqlSession#getMapper()

public T getObject() throws Exception {    return getSqlSession().getMapper(this.mapperInterface);}

上面我們分析了如何將Mapper接口類注入到IoC容器中的實現思路,現在總結下主要有:

基于BeanDefinitionRegistryPostProcessor接口實現擴展,然后動態向IoC容器中注入Bean;在注入時,會使用到ClassPathMapperScanner類掃描器將所有的Mapper接口類解析成BeanDefinition集合注入;為了解決接口不能創建對象問題,再注入后又將BeanDefinitionbeanClass替換成FactoryBean的實現類:MapperFactoryBean,在該實現類中通過mybatis apiSqlSession#getMapper()獲取到Mapper接口的動態代理類

擴展點引入

通過MapperScannerConfigurer,解決了如何將Mapper接口類注入到IoC容器的問題,現在還有另外一個問題,這個擴展點只有注冊到Spring中才會起作用,那又如何將其注冊到Spring中呢?

方式一:最直接方式就是直接創建MapperScannerConfigurer類型的Bean實例,比如:

        

這種方式是最簡單直接的,但是使用角度來說不方便,所以,mybatis-spring-1.2新增了兩種方式:標簽方式和@MapperScan注解方式。

首先來看下標簽方式,添加mybatisschema,然后就可以使用:

                                                                    

后臺處理類NamespaceHandler標簽注冊解析器MapperScannerBeanDefinitionParser

public class NamespaceHandler extends NamespaceHandlerSupport {  @Override  public void init() {    registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser());  }}

再看下MapperScannerBeanDefinitionParser解析器:

protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);    ClassLoader classLoader = ClassUtils.getDefaultClassLoader();    builder.addPropertyValue("processPropertyPlaceHolders", true);    try {      String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION);      if (StringUtils.hasText(annotationClassName)) {        @SuppressWarnings("unchecked")        Class annotationClass = (Class) classLoader            .loadClass(annotationClassName);        builder.addPropertyValue("annotationClass", annotationClass);      }      String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE);      if (StringUtils.hasText(markerInterfaceClassName)) {        Class markerInterface = classLoader.loadClass(markerInterfaceClassName);        builder.addPropertyValue("markerInterface", markerInterface);      }      String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR);      if (StringUtils.hasText(nameGeneratorClassName)) {        Class nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName);        BeanNameGenerator nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class);        builder.addPropertyValue("nameGenerator", nameGenerator);      }      String mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS);      if (StringUtils.hasText(mapperFactoryBeanClassName)) {        @SuppressWarnings("unchecked")        Class mapperFactoryBeanClass = (Class) classLoader            .loadClass(mapperFactoryBeanClassName);        builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);      }    } catch (Exception ex) {      XmlReaderContext readerContext = parserContext.getReaderContext();      readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());    }    builder.addPropertyValue("sqlSessionTemplateBeanName", element.getAttribute(ATTRIBUTE_TEMPLATE_REF));    builder.addPropertyValue("sqlSessionFactoryBeanName", element.getAttribute(ATTRIBUTE_FACTORY_REF));    builder.addPropertyValue("lazyInitialization", element.getAttribute(ATTRIBUTE_LAZY_INITIALIZATION));    builder.addPropertyValue("basePackage", element.getAttribute(ATTRIBUTE_BASE_PACKAGE));    return builder.getBeanDefinition();  }

最關鍵的就是第一句BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);,又是將MapperScannerConfigurer動態注入到Spring中,下面一堆都是解析標簽屬性進行依賴注入。

再來看下@MapperScan注解方式,如:@MapperScan(basePackages = "org.simon.demo01.mapper")

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(MapperScannerRegistrar.class)public @interface MapperScan {}

@MapperScan注解上面使用了使用了一種非常常見的擴展方式:@Import擴展。通過@Import注解,引入了MapperScannerRegistrar,它是ImportBeanDefinitionRegistrar類型,通常和@Import注解組合使用,實現動態注入功能:

@Override  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {    //獲取注解上屬性    AnnotationAttributes mapperScanAttrs = AnnotationAttributes        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));    if (mapperScanAttrs != null) {      registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));    }  }  void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {    //創建一個MapperScannerConfigurer的BeanDefinition    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);    /**     * 下面就是解析注解屬性值,通過PropertyValue方式進行依賴注入到Bean中     */    builder.addPropertyValue("processPropertyPlaceHolders", true);    Class annotationClass = annoAttrs.getClass("annotationClass");    if (!Annotation.class.equals(annotationClass)) {      builder.addPropertyValue("annotationClass", annotationClass);    }    Class markerInterface = annoAttrs.getClass("markerInterface");    if (!Class.class.equals(markerInterface)) {      builder.addPropertyValue("markerInterface", markerInterface);    }    ...//各種依賴注入    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));    //將生成的BeanDefinition注冊到IoC中    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());}

方法中同樣有BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class)這句,動態的將MapperScannerConfigurer注入到Spring中,然后是一堆的解析注解屬性進行依賴注入,這樣通過@Import+ImportBeanDefinitionRegistrar動態注入,就實現了將MapperScannerConfigurer擴展點注冊到Spring中。

SpringBoot自動裝配

不論是通過標簽方式,還是@MapperScan注解方式,這些是常規的第三方模塊與Spring進行集成方式。這種集成方式比較繁瑣的是:你不光要通過@MapperScan注解將第三方集成進來,你還需要初始化一些依賴對象,比如這里的DataSourceSqlSessionFactory等。當一個項目集成了很多第三方模塊時,每個模塊都這樣搞一下,配置的工作量就大了,比如最常使用的ssm集成配,傳統Spring集成要搞一大堆配置。

所以,SpringBoot提出了一個比較優秀的思想:自動裝配。需要什么模塊直接把依賴添加進來,自動完成裝配,對于個性化可以在屬性文件中進行配置,從使用角度來說,即插即用,不需要有太多的編碼。第三方程序和spring就像完全融入一體一樣,簡化項目構建時集成成本,也降低項目配置的復雜性,所以SpringBoot會被越來越多的項目所采用,進而也推動微服務的興起。

SpringBoot中使用mybatis,直接依賴mybatis-spring-boot-starter,它會把mybatismybatis-springmybatis-spring-boot-autoconfigure三個依賴包都添加進來。前面兩個依賴包好理解,這里關鍵是第三個依賴包,就是通過它實現了mybatis自動裝配功能。下面我們來看下SpringBoot是如何實現mybatis的主動裝配。

1、首先,定義一個mybatis主動裝配配置類,如下:

@org.springframework.context.annotation.Configuration@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })@ConditionalOnSingleCandidate(DataSource.class)@EnableConfigurationProperties(MybatisProperties.class)@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })public class MybatisAutoConfiguration implements InitializingBean {  public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider,      ObjectProvider typeHandlersProvider, ObjectProvider languageDriversProvider,      ResourceLoader resourceLoader, ObjectProvider databaseIdProvider,      ObjectProvider> configurationCustomizersProvider) { ...  }  @Bean  @ConditionalOnMissingBean  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();    factory.setDataSource(dataSource);    factory.setVfs(SpringBootVFS.class);    if (StringUtils.hasText(this.properties.getConfigLocation())) {      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));    }    applyConfiguration(factory);    ...//省略一堆配置    if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {      factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);    }    return factory.getObject();  }  @Bean  @ConditionalOnMissingBean  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {    ExecutorType executorType = this.properties.getExecutorType();    if (executorType != null) {      return new SqlSessionTemplate(sqlSessionFactory, executorType);    } else {      return new SqlSessionTemplate(sqlSessionFactory);    }  }    public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {    private BeanFactory beanFactory;    @Override    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {      if (!AutoConfigurationPackages.has(this.beanFactory)) {        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");        return;      }      logger.debug("Searching for mappers annotated with @Mapper");      List packages = AutoConfigurationPackages.get(this.beanFactory);      if (logger.isDebugEnabled()) {        packages.forEach(pkg -> logger.debug("Using auto-configuration base package "{}"", pkg));      }      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);      builder.addPropertyValue("processPropertyPlaceHolders", true);      builder.addPropertyValue("annotationClass", Mapper.class);      builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));      BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);      Stream.of(beanWrapper.getPropertyDescriptors())          .filter(x -> x.getName().equals("lazyInitialization")).findAny()          .ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"));      registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());    }    @Override    public void setBeanFactory(BeanFactory beanFactory) {      this.beanFactory = beanFactory;    }  }  @org.springframework.context.annotation.Configuration  @Import(AutoConfiguredMapperScannerRegistrar.class)  @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })  public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {  }}

這里主要利用MapperScannerRegistrarNotFoundConfiguration類上的@Import(AutoConfiguredMapperScannerRegistrar.class)引入,然后在AutoConfiguredMapperScannerRegistrarBeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class)這句又是動態注入MapperScannerConfigurer。不過,主動裝配配置類中,還會把相關的依賴也一起創建、初始化,比如:SqlSessionFactorySqlSessionTemplate

@EnableConfigurationProperties(MybatisProperties.class)mybatis相關配置引入進來,這樣在創建、初始化過程中的定制需求就可以通過配置修改。

2、有了這個主動裝配配置類還不行,下一步就是看如何讓主動裝配配置類生效。SpringBoot提供了SpringFactoriesLoader工廠加載機制,類似于JDK中的SPI機制,實現將模塊META-INF/spring.factories文件中配置注入到Spring容器中。mybatis-spring-boot-autoconfigure模塊下META-INF/spring.factories文件中就有MybatisAutoConfiguration

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

3、使用時依賴添加進來,配置下屬性,就可以直接使用,基本不再需要編碼:

mybatis.mapper-locations: classpath:mapper/*Mapper.xmlmybatis.type-aliases-package: com.example.demo.entity

總結

從上面來看,mybatisspring集成的關鍵的是將mybatis-spring模塊下MapperScannerConfigurer集成進來,因為,它是一個BeanDefinitionRegistryPostProcessor類型的擴展,內部通過自定義scanner掃描Mapper接口自動注冊到IoC容器中,這一點在各種集成方式中是統一一樣的。不同點在于:MapperScannerConfigurer擴展類是如何被引入的。傳統的Spring方式通過@Mapper注解或自定義標簽實現,但是對于一些依賴對象還是需要手工創建,比較繁瑣;而SpringBoot利用自動裝配,讓第三方模塊集成變成了一個插件,即插即用,無需太多編碼。

分析了mybatis集成方式,從中也學習了如何利用Spring的各種擴展點進行定制,更重要的是也為我們開發自己模塊和Spring集成提供了思路。

關鍵詞:

相關文章

熱文推薦

世界訊息:【Spring源碼】- 08 擴展點之mybatis集成
世界訊息:【Spring源碼】- 08 擴展點之mybatis集成

mybatis將與spring集成的代碼拆分到了mybatis-spring......更多>

當前快報:鄭州市房協出臺房企信用激勵措施:優先推薦AAA級房企參與保障性住房建設
當前快報:鄭州市房協出臺房企信用激勵措施:優先推薦AAA級房企參與保障性住房建設

鄭州市房協公告,對房地產企業實行信用激勵措施。其中......更多>

萬達信息:公司已形成數百個人工智能行業模型
萬達信息:公司已形成數百個人工智能行業模型

萬達信息(300168)在互動平臺表示,公司對人工智能(161......更多>

焦點熱議:工信部:1-2月電子信息制造業生產規模同比小幅收縮
焦點熱議:工信部:1-2月電子信息制造業生產規模同比小幅收縮

據工信部消息,1-2月份,我國電子信息制造業生產規模......更多>

排行推薦

世界微速訊:威馬汽車被凍結40億股權
世界微速訊:威馬汽車被凍結40億股權
天眼查顯示,近日,威馬汽車科技集團有限公司新增一則... 更多>
周小川:理論上碳減排工具有最優選擇,我們需要尋找最優解
周小川:理論上碳減排工具有最優選擇,我們需要尋找最優解
博鰲亞洲論壇副理事長、人民銀行原行長周小川在博鰲亞... 更多>
王傳福:比亞迪一季度銷量仍會同比增長80%以上
王傳福:比亞迪一季度銷量仍會同比增長80%以上
3月29日的業績發布會上,比亞迪(002594)董事長王傳福... 更多>
國家發改委產業司召開推動農機裝備產業高質量發展座談會_當前信息
國家發改委產業司召開推動農機裝備產業高質量發展座談會_當前信息
國家發改委產業司負責同志近日主持召開推動農機裝備產... 更多>
理想的基金投顧什么樣?
最近參加興全基金投顧主理人張濟民總的一場內部分享會... 更多>
你知道李耀一辦公室在哪里嗎?:全球頭條
《進階吧!投資者》第二十六話中,陳丙伸給李耀一打電... 更多>
世界看點:昨天投資世界發生了什么?
2023年3月29日星期三1、倒閉后被收購的硅谷銀行和簽名... 更多>
環球速讀:11部門:積極將地方特色食品消費元素嵌入夜間經濟等消費場景
工信部等11部門近日聯合發布了《關于培育傳統優勢食品... 更多>
水利部:2023年汛期長江流域旱重于澇
水利部長江委28日召開會商會,分析研判今年汛期長江流... 更多>
光伏板塊走強 奧克股份漲超12%:世界觀天下
光伏板塊開盤走強,奧克股份漲超12%,珈偉新能、岱勒... 更多>
“貴惠保”公益助殘行動慰問殘疾人代表 世界新消息
中新網貴州新聞3月28日電(記者張偉)“貴惠保”公益... 更多>
【播資訊】北京一季度辦公樓帶看量增長2倍,租金預期差距仍阻礙成交轉化
3月28日,高力國際發布的第一季度北京辦公樓市場數據... 更多>
江蘇:今年安排9個機場建設項目,對蘇州機場等深化前期研究|天天觀熱點
從江蘇省民航工作座談會上獲悉,2023年江蘇計劃投資26... 更多>
天天觀焦點:蒙泰高新聯合電氣風電共設新材料科技公司,注冊資本5.5億
企查查APP顯示,近日,上海納塔新材料科技有限公司成... 更多>

贛能股份投資設立新公司 經營范圍

北斗導航概念股開盤下挫

半導體板塊開盤走低

從預期說起-淺談淮北2022年報

信息:快狗打車,是價值洼地還是價

一個新時代已經到來——AIGC已經開

熱點評!中信證券:綠氫產業高速成

兩市融資余額增加15.66億元 世界快看

焦點!微軟舉辦峰會,大模型賦能網

安徽杭蕭簽約上海正泰智電港項目(

亚洲欧美综合网_国产丶欧美丶日本不卡视频_久久精品亚洲精品国产欧美kt∨ _国产精品自在欧美一区_欧美成人精品激情在线观看 _婷婷丁香久久五月婷婷_国产一本一道久久香蕉_国产www精品_亚洲国产精品久久久久秋霞蜜臀_国产精品xxxxx
欧美精品乱人伦久久久久久| 亚洲精品视频观看| 欧美精品乱人伦久久久久久| 亚洲日本青草视频在线怡红院| 精品一区二区在线播放| 欧美区在线观看| 亚洲成年人影院| 91精品国产色综合久久不卡蜜臀| 亚洲福利一区二区三区| 欧美日韩激情一区| 老汉av免费一区二区三区| 欧美一卡二卡在线| 国产麻豆视频一区二区| 国产人妖乱国产精品人妖| 99久久久精品| 首页国产丝袜综合| 久久在线免费观看| 91免费视频大全| 午夜精品影院在线观看| 欧美mv和日韩mv的网站| 99国产精品久久| 麻豆久久一区二区| 亚洲柠檬福利资源导航| 91精品在线免费| 国产一区二区精品久久99| 亚洲免费观看高清完整版在线观看| 欧美色成人综合| 成人一级片网址| 亚洲国产你懂的| 中文字幕国产一区二区| 欧美日韩免费视频| 成人av一区二区三区| 奇米精品一区二区三区在线观看| 中日韩av电影| 欧美一区二区在线不卡| 色播五月激情综合网| 成人在线视频一区二区| 久久69国产一区二区蜜臀| 亚洲成人自拍一区| 亚洲美女淫视频| 国产精品午夜免费| 欧美精品一区二区久久婷婷| 欧美性一二三区| 91高清视频免费看| 91在线国内视频| 99麻豆久久久国产精品免费优播| 极品瑜伽女神91| 美女性感视频久久| 日本成人中文字幕在线视频 | 亚洲无人区一区| ...av二区三区久久精品| 亚洲国产精品高清| 成人欧美一区二区三区| 国产精品福利在线播放| 国产精品久久久久久久久免费桃花 | 欧美人伦禁忌dvd放荡欲情| 色噜噜狠狠成人网p站| 99久久99久久精品国产片果冻 | 99精品久久免费看蜜臀剧情介绍| 福利一区二区在线| 成人性色生活片| 欧美在线一区二区| 日韩欧美中文字幕制服| 久久五月婷婷丁香社区| 中文字幕一区二区三区色视频| 亚洲欧美日韩电影| 奇米亚洲午夜久久精品| 成人免费黄色在线| 91黄色免费网站| 亚洲精品一区二区三区蜜桃下载| 国产精品久久看| 日韩精品午夜视频| 不卡av在线网| 欧美一级高清大全免费观看| 国产精品女主播在线观看| 午夜精品视频一区| 不卡一区二区在线| 日韩欧美成人激情| 一区二区三区中文字幕| 精油按摩中文字幕久久| 99re成人精品视频| 欧美日韩大陆一区二区| 国产欧美精品在线观看| 日本在线不卡视频| 色婷婷精品久久二区二区蜜臂av| 日韩精品最新网址| 亚洲女子a中天字幕| 欧美一级xxx| 91精品国产综合久久久蜜臀图片 | 国产成人在线影院| 波波电影院一区二区三区| 欧洲av在线精品| 国产三级久久久| 秋霞午夜鲁丝一区二区老狼| 国产成人亚洲精品青草天美| 91黄色免费看| 国产欧美日韩久久| 久久国产欧美日韩精品| 欧美日韩一区二区电影| 亚洲欧美日韩系列| 粗大黑人巨茎大战欧美成人| 欧美tickling网站挠脚心| 午夜欧美视频在线观看| 色网站国产精品| 亚洲天堂精品在线观看| 成人激情小说网站| 国产精品素人视频| 不卡免费追剧大全电视剧网站| 亚洲精品在线免费观看视频| 欧美大度的电影原声| 亚洲国产成人一区二区三区| 日韩国产欧美在线播放| 精品视频一区二区三区免费| 亚洲精品欧美综合四区| 91在线看国产| 亚洲福利一区二区| 欧美精品乱码久久久久久按摩| 亚洲一区二区三区免费视频| 91色乱码一区二区三区| 亚洲另类色综合网站| 91福利社在线观看| 午夜激情一区二区三区| 欧美老女人第四色| 久久97超碰国产精品超碰| 精品美女一区二区三区| 成人午夜视频免费看| 国产精品―色哟哟| 在线观看免费亚洲| 秋霞午夜鲁丝一区二区老狼| 日韩你懂的在线播放| 国产精品18久久久久| 日本一区二区三级电影在线观看| 国产91综合一区在线观看| 亚洲老妇xxxxxx| 日韩女同互慰一区二区| 大胆亚洲人体视频| 日韩一区欧美二区| 中文字幕日韩一区| 884aa四虎影成人精品一区| 国模娜娜一区二区三区| 一区二区三区四区中文字幕| 91精品国模一区二区三区| 成人性色生活片免费看爆迷你毛片| 亚洲一区二区三区自拍| 久久久久久一二三区| 色视频成人在线观看免| 麻豆视频一区二区| 伊人夜夜躁av伊人久久| 国产三级三级三级精品8ⅰ区| 欧美日韩国产精品自在自线| 激情综合网av| 日韩电影在线一区| 国产亚洲美州欧州综合国| 欧美猛男男办公室激情| 成人av综合在线| 极品美女销魂一区二区三区免费 | 国产制服丝袜一区| 性欧美大战久久久久久久久| 国产精品电影院| 国产亚洲一本大道中文在线| 91精品国产综合久久久蜜臀图片 | 久久精品夜色噜噜亚洲aⅴ| 91精品国产综合久久久久久漫画| av资源网一区| 国产91精品露脸国语对白| 国产一区二区剧情av在线| 日韩电影免费在线观看网站| 亚洲一区二区黄色| 一区二区三区不卡视频在线观看| 国产精品网站在线观看| 中文字幕亚洲一区二区av在线| 久久精品日韩一区二区三区| 精品电影一区二区| 久久久久久久久岛国免费| 久久久不卡网国产精品一区| 国产肉丝袜一区二区| 国产精品国产三级国产普通话蜜臀 | a级高清视频欧美日韩| 不卡一区二区三区四区| 不卡的av电影在线观看| 一本色道综合亚洲| 欧美日韩一区二区在线观看视频| 欧美视频一区二区三区四区| 欧美一区二区视频网站| 久久免费视频一区| 自拍av一区二区三区| 亚洲精品视频在线| 国产成人在线网站| 久久免费偷拍视频| 国产精品主播直播| 国产91清纯白嫩初高中在线观看 | 国产一区二区在线视频| 天天色综合天天| 日本欧美一区二区三区| 久久99久久久久| 国产suv精品一区二区三区| 国产99久久久国产精品潘金| 色婷婷国产精品综合在线观看| 欧美美女激情18p| 欧美激情资源网|