<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          springboot的啟動(dòng)流程源碼分析

          共 10855字,需瀏覽 22分鐘

           ·

          2020-10-02 18:47

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|??yangxiaohui227?

          來(lái)源 |? urlify.cn/6jeaiu

          66套java從入門到精通實(shí)戰(zhàn)課程分享

          測(cè)試項(xiàng)目,隨便一個(gè)簡(jiǎn)單的springboot項(xiàng)目即可:

          ?直接debug調(diào)試:

          ?可見(jiàn),分2步,第一步是創(chuàng)建SpringApplication對(duì)象,第二步是調(diào)用run方法:

          1.SpringApplication對(duì)象的創(chuàng)建過(guò)程:

          public?SpringApplication(ResourceLoader?resourceLoader,?Class...?primarySources)?{?//resourceLoader為null,因?yàn)槲覀儧](méi)有傳入,primarySources這里包含主啟動(dòng)類的ThymeleafApplication.class
          ????????this.resourceLoader?=?resourceLoader;?//資源加載器,這里是null
          ????????Assert.notNull(primarySources,?"PrimarySources?must?not?be?null");
          ????????this.primarySources?=?new?LinkedHashSet<>(Arrays.asList(primarySources));?//將主啟動(dòng)類字節(jié)碼存起來(lái)
          ????????this.webApplicationType?=?WebApplicationType.deduceFromClasspath();?//檢測(cè)當(dāng)前的項(xiàng)目web類型,后續(xù)會(huì)分析
          ????????setInitializers((Collection)?getSpringFactoriesInstances(ApplicationContextInitializer.class));//這里涉及springboot的一個(gè)重要知識(shí)點(diǎn),后續(xù)分析
          ????????setListeners((Collection)?getSpringFactoriesInstances(ApplicationListener.class));//這里涉及springboot的一個(gè)重要知識(shí)點(diǎn),后續(xù)分析
          ????????this.mainApplicationClass?=?deduceMainApplicationClass();//這里檢測(cè)main方法所在的類
          ????}

          通過(guò)SpringApplication的創(chuàng)建過(guò)程,我們分析下,它的主要幾個(gè)方法:

          this.webApplicationType =?WebApplicationType.deduceFromClasspath();

          ? 因?yàn)槲乙氲氖莝pringboot-web相關(guān)依賴,所以,在本次測(cè)試項(xiàng)目中,webApplication的類型是AnnotationConfigServletWebServerApplicationContext

          ?

           setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class))和setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)):

          后續(xù)很多地方也會(huì)用到這個(gè)功能,在此解析下:

          所以上面2個(gè)方法分別是從spring.factories文件中,找到key為org.springframework.context.ApplicationContextInitializer 的所有值,然后創(chuàng)建對(duì)應(yīng)的實(shí)例,另一個(gè)ApplicationListener同理

          ?

          接下來(lái)我們分析下:this.mainApplicationClass = deduceMainApplicationClass();

          檢測(cè)main方法所在的類,我們自己寫的代碼,自己肯定很容易知道是哪個(gè)類,但springboot框架不知道,那怎么檢測(cè)呢,我們先看一個(gè)異常棧信息:

          ??

          ?如上圖所示,我們只要從一個(gè)異常的堆棧中就可以獲取到main方法了,所以源碼檢測(cè)main方法也是一樣的:

          ?

          ?至此SpringApplication對(duì)象創(chuàng)建完畢,后續(xù)我們分析下它的run方法都做了些啥:

          public?ConfigurableApplicationContext?run(String...?args)?{
          ????????StopWatch?stopWatch?=?new?StopWatch();?//一個(gè)計(jì)時(shí)器,用于計(jì)算啟動(dòng)的時(shí)間
          ????????stopWatch.start();//計(jì)時(shí)開(kāi)始
          ????????ConfigurableApplicationContext?context?=?null;
          ????????Collection?exceptionReporters?=?new?ArrayList<>();//異常報(bào)告器列表
          ??????? configureHeadlessProperty();//這個(gè)是給System對(duì)象設(shè)置一個(gè)屬性:java.awt.headless=true
          ????????SpringApplicationRunListeners?listeners?=?getRunListeners(args);?//從spring.factories文件中讀取SpringApplicationRunListener的實(shí)現(xiàn)類,并創(chuàng)建對(duì)應(yīng)的實(shí)例,這個(gè)類后續(xù)分析
          ????????listeners.starting();//調(diào)用監(jiān)聽(tīng)器的starting方法
          ????????try?{
          ????????????ApplicationArguments?applicationArguments?=?new?DefaultApplicationArguments(args);?//這里僅僅是對(duì)請(qǐng)求參數(shù)進(jìn)行封裝成一個(gè)對(duì)象
          ????????????ConfigurableEnvironment?environment?=?prepareEnvironment(listeners,?applicationArguments);//創(chuàng)建并配置環(huán)境的一些屬性,然后調(diào)用監(jiān)聽(tīng)器的相應(yīng)方法,后續(xù)會(huì)分析
          ????????????configureIgnoreBeanInfo(environment);//往System對(duì)象中設(shè)置屬性spring.beaninfo.ignore的值
          ????????????Banner?printedBanner?=?printBanner(environment);//打印啟動(dòng)的圖標(biāo),后續(xù)分析
          ??????????? context = createApplicationContext();//在創(chuàng)建SpringApplication對(duì)象時(shí),已經(jīng)檢測(cè)出當(dāng)前環(huán)境是什么樣的webAppliction的類型,這里就是創(chuàng)建該類型的實(shí)例,本次代碼演示的是:AnnotationConfigServletWebServerApplicationContext
          ????????????exceptionReporters?=?getSpringFactoriesInstances(SpringBootExceptionReporter.class,
          ????????????????????new?Class[]?{?ConfigurableApplicationContext.class?},?context);//從spring.factories文件中讀取SpringBootExceptionReporter的實(shí)現(xiàn)類,并創(chuàng)建對(duì)應(yīng)的實(shí)例
          ????????????prepareContext(context,?environment,?listeners,?applicationArguments,?printedBanner);//為后續(xù)實(shí)例化所有的bean進(jìn)行初始化工作,后續(xù)分析
          ????????????refreshContext(context);//這里開(kāi)始實(shí)例化所有的bean,也就是spring?ioc的核心,調(diào)用父類的refresh()方法,同時(shí)對(duì)tomcat進(jìn)行了實(shí)例化,這些源碼我都有專門的博客分析過(guò)了,所以這里不再重復(fù)
          ????????????afterRefresh(context,?applicationArguments);?//目前是空實(shí)現(xiàn)
          ????????????stopWatch.stop();?//計(jì)時(shí)結(jié)束
          ????????????if?(this.logStartupInfo)?{
          ??????????????? new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);//打印本次啟動(dòng)花了多長(zhǎng)時(shí)間:Root WebApplicationContext: initialization completed in?4186?ms
          ????????????}
          ????????????listeners.started(context);//調(diào)用Listenners的stared方法
          ????????????callRunners(context,?applicationArguments);//調(diào)用ApplicationRunner和CommandLineRunner的方法
          ????????}
          ????????catch?(Throwable?ex)?{
          ????????????handleRunFailure(context,?ex,?exceptionReporters,?listeners);
          ????????????throw?new?IllegalStateException(ex);
          ????????}

          ????????try?{
          ????????????listeners.running(context);//調(diào)用監(jiān)聽(tīng)器的running方法
          ????????}
          ????????catch?(Throwable?ex)?{
          ????????????handleRunFailure(context,?ex,?exceptionReporters,?null);
          ????????????throw?new?IllegalStateException(ex);
          ????????}
          ????????return?context;
          ????}

          通過(guò)上面的分析,整個(gè)啟動(dòng)流程在原來(lái)spring啟動(dòng)項(xiàng)目的基礎(chǔ)上,在不同的階段,加上了監(jiān)聽(tīng)器的各個(gè)方法調(diào)用,現(xiàn)在我們來(lái)詳細(xì)分析上面的一些方法:

          ?SpringApplicationRunListeners 和? SpringApplicationRunListener 關(guān)系,很明顯,前者包含了多個(gè)后者:

          class?SpringApplicationRunListeners?{

          ????private?final?Log?log;

          ????private?final?List?listeners;

          ????SpringApplicationRunListeners(Log?log,?Collection?listeners)?{
          ????????this.log?=?log;
          ????????this.listeners?=?new?ArrayList<>(listeners);
          ????}

          ????void?starting()?{
          ????????for?(SpringApplicationRunListener?listener?:?this.listeners)?{
          ????????????listener.starting();?//開(kāi)始啟動(dòng),此時(shí)環(huán)境和上下文都還沒(méi)開(kāi)始啟動(dòng)
          ????????}
          ????}

          ????void?environmentPrepared(ConfigurableEnvironment?environment)?{
          ????????for?(SpringApplicationRunListener?listener?:?this.listeners)?{
          ????????????listener.environmentPrepared(environment);??//環(huán)境已經(jīng)準(zhǔn)備好了,我們可以通過(guò)實(shí)現(xiàn)對(duì)應(yīng)的listener接口來(lái)修改環(huán)境中變量
          ????????}
          ????}

          ????void?contextPrepared(ConfigurableApplicationContext?context)?{
          ????????for?(SpringApplicationRunListener?listener?:?this.listeners)?{
          ????????????listener.contextPrepared(context);?//上下文已經(jīng)準(zhǔn)備好
          ????????}
          ????}

          ????void?contextLoaded(ConfigurableApplicationContext?context)?{
          ????????for?(SpringApplicationRunListener?listener?:?this.listeners)?{
          ????????????listener.contextLoaded(context);//上下文已經(jīng)加載完畢
          ????????}
          ????}

          ????void?started(ConfigurableApplicationContext?context)?{
          ????????for?(SpringApplicationRunListener?listener?:?this.listeners)?{
          ????????????listener.started(context);?//上下文已經(jīng)啟動(dòng)完畢
          ????????}
          ????}

          ????void?running(ConfigurableApplicationContext?context)?{
          ????????for?(SpringApplicationRunListener?listener?:?this.listeners)?{
          ????????????listener.running(context);//上下文啟動(dòng)完畢后,進(jìn)入運(yùn)行狀態(tài)
          ????????}
          ????}

          ????void?failed(ConfigurableApplicationContext?context,?Throwable?exception)?{
          ????????for?(SpringApplicationRunListener?listener?:?this.listeners)?{
          ????????????callFailedListener(listener,?context,?exception);
          ????????}
          ????}

          ????private?void?callFailedListener(SpringApplicationRunListener?listener,?ConfigurableApplicationContext?context,
          ????????????Throwable?exception)?{
          ????????try?{
          ????????????listener.failed(context,?exception);//啟動(dòng)失敗做些啥
          ????????}
          ????????catch?(Throwable?ex)?{
          ????????????if?(exception?==?null)?{
          ????????????????ReflectionUtils.rethrowRuntimeException(ex);
          ????????????}
          ????????????if?(this.log.isDebugEnabled())?{
          ????????????????this.log.error("Error?handling?failed",?ex);
          ????????????}
          ????????????else?{
          ????????????????String?message?=?ex.getMessage();
          ????????????????message?=?(message?!=?null)???message?:?"no?error?message";
          ????????????????this.log.warn("Error?handling?failed?("?+?message?+?")");
          ????????????}
          ????????}
          ????}

          }

          可見(jiàn)SpringApplicationRunListener 會(huì)在不同的啟動(dòng)周期調(diào)用不同的方法,不必非常刻意理解每個(gè)方法的具體含義,我們可以根據(jù)每個(gè)方法在源碼中的調(diào)用位置,自己實(shí)現(xiàn)一個(gè)SpringApplicationRunListener做一些個(gè)性化的修改

          接下來(lái),我們分析下:ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);方法,準(zhǔn)備環(huán)境對(duì)象

          private?ConfigurableEnvironment?prepareEnvironment(SpringApplicationRunListeners?listeners,
          ????????????ApplicationArguments?applicationArguments)?{
          ????????//?Create?and?configure?the?environment
          ????????ConfigurableEnvironment?environment?=?getOrCreateEnvironment();//獲取一個(gè)環(huán)境對(duì)象,不存在就創(chuàng)建
          ????????configureEnvironment(environment,?applicationArguments.getSourceArgs());//進(jìn)行命令行參數(shù)配置,我們命令行參數(shù)沒(méi)數(shù)據(jù)是一個(gè)空數(shù)組,所以不會(huì)做什么,這里主要的設(shè)置了ConversionService對(duì)象到enviroment中
          ????????ConfigurationPropertySources.attach(environment);//配置configurationProperties
          ????????listeners.environmentPrepared(environment);?//監(jiān)聽(tīng)器處理環(huán)境準(zhǔn)備好事件
          ????????bindToSpringApplication(environment);//綁定environment到SpringApplication中
          ????????if?(!this.isCustomEnvironment)?{
          ????????????environment?=?new?EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
          ????????????????????deduceEnvironmentClass());
          ????????}
          ????????ConfigurationPropertySources.attach(environment);
          ????????return?environment;
          ????}

          下面看看Banner printedBanner = printBanner(environment); 效果如下:我們也可以自定義

          private?Banner?printBanner(ConfigurableEnvironment?environment)?{
          ????????if?(this.bannerMode?==?Banner.Mode.OFF)?{?//如果是設(shè)置了不打印banner,這里就不會(huì)打印
          ????????????return?null;
          ????????}
          ????????ResourceLoader?resourceLoader?=?(this.resourceLoader?!=?null)???this.resourceLoader
          ????????????????:?new?DefaultResourceLoader(getClassLoader());?//資源加載器
          ????????SpringApplicationBannerPrinter?bannerPrinter?=?new?SpringApplicationBannerPrinter(resourceLoader,?this.banner);?//創(chuàng)建banner打印器
          ????????if?(this.bannerMode?==?Mode.LOG)?{
          ????????????return?bannerPrinter.print(environment,?this.mainApplicationClass,?logger);?//打印到日志文件
          ????????}
          ????????return?bannerPrinter.print(environment,?this.mainApplicationClass,?System.out);//打印到控制臺(tái),這里我們以打印到控制臺(tái)來(lái)根據(jù)
          ????}

          繼續(xù)分析:bannerPrinter.print(environment, this.mainApplicationClass, System.out);

          Banner?print(Environment?environment,?Class?sourceClass,?PrintStream?out)?{
          ????????Banner?banner?=?getBanner(environment);?//從環(huán)境中獲取banner
          ????????banner.printBanner(environment,?sourceClass,?out);?//打印banner?注意Banner是一個(gè)接口,這里會(huì)根據(jù)子類來(lái)調(diào)用不同的打印方法,如圖片banner和文本banner是不一樣的
          ????????return?new?PrintedBanner(banner,?sourceClass);
          ????}

          現(xiàn)在重點(diǎn)在于如何獲取banner:getBanner(environment);

          private?Banner?getBanner(Environment?environment)?{
          ????????Banners?banners?=?new?Banners();
          ????????banners.addIfNotNull(getImageBanner(environment));?//獲取圖片banner
          ????????banners.addIfNotNull(getTextBanner(environment));//獲取文本banner
          ????????if?(banners.hasAtLeastOneBanner())?{
          ????????????return?banners;
          ????????}
          ????????if?(this.fallbackBanner?!=?null)?{
          ????????????return?this.fallbackBanner;
          ????????}
          ????????return?DEFAULT_BANNER;?//都獲取不到,就用默認(rèn)的banner
          ????}

          //看看圖片banner是如何獲取的

          ?

          ?接下來(lái),我們看看文本banner的獲取:

          ?當(dāng)加載不到默認(rèn)的圖片banner或者文本banner就會(huì)使用默認(rèn)的banner,我們看看默認(rèn)的banner是啥:

          ?

          ?

          ?所以,如果我們要打印自定義的banner,只要在resources文件夾下加入banner.txt ?或者banner.gif/banner.jpg/banner.png即可:
          接下來(lái),我們分析:prepareContext(context, environment, listeners, applicationArguments, printedBanner);準(zhǔn)備上下文:

          private?void?prepareContext(ConfigurableApplicationContext?context,?ConfigurableEnvironment?environment,
          ????????????SpringApplicationRunListeners?listeners,?ApplicationArguments?applicationArguments,?Banner?printedBanner)?{
          ????????context.setEnvironment(environment);?//給context設(shè)置環(huán)境對(duì)象,context在這里是AnnotationConfigServletWebServerApplicationContext
          ????????postProcessApplicationContext(context);//這里根據(jù)條件給context設(shè)置一些重要的對(duì)象
          ????????applyInitializers(context);//Spring.factories文件中配置的ApplicationContextInitializer實(shí)現(xiàn)類,對(duì)context進(jìn)行一些初始化操作,我們想對(duì)context進(jìn)行特定操作也可以通過(guò)這種方式
          ????????listeners.contextPrepared(context);//監(jiān)聽(tīng)器打印上下文已經(jīng)準(zhǔn)備好事件
          ????????if?(this.logStartupInfo)?{
          ????????????logStartupInfo(context.getParent()?==?null);
          ????????????logStartupProfileInfo(context);//打印日志,當(dāng)前啟動(dòng)的是那個(gè)yml??“No?active?profile?set,?falling?back?to?default?profiles:?default”
          ????????}
          ????????//?Add?boot?specific?singleton?beans
          ????????ConfigurableListableBeanFactory?beanFactory?=?context.getBeanFactory();//獲取bean工廠
          ????????beanFactory.registerSingleton("springApplicationArguments",?applicationArguments);//注冊(cè)bean
          ????????if?(printedBanner?!=?null)?{
          ????????????beanFactory.registerSingleton("springBootBanner",?printedBanner);?//注冊(cè)banner這個(gè)bean,這里比較有趣了,后續(xù)你可以通過(guò)beanFactory獲得該bean,然后繼續(xù)打印banner
          ????????}
          ????????if?(beanFactory?instanceof?DefaultListableBeanFactory)?{
          ????????????((DefaultListableBeanFactory)?beanFactory)
          ????????????????????.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
          ????????}
          ????????if?(this.lazyInitialization)?{
          ????????????context.addBeanFactoryPostProcessor(new?LazyInitializationBeanFactoryPostProcessor());
          ????????}
          ????????//?Load?the?sources
          ????????Set?sources?=?getAllSources();?//這里的source是指main方法所在類(ThymeleafApplication),SpringApplication.run(ThymeleafApplication.class,?args);
          ????????Assert.notEmpty(sources,?"Sources?must?not?be?empty");
          ??????? load(context, sources.toArray(new Object[0]));//?為何要加載呢?原因是主啟動(dòng)類貼有@SpringBootApplication等注解,該啟動(dòng)類需要注冊(cè)成一個(gè)配置bean,并解析它的注解
          ????????listeners.contextLoaded(context);//上下文加載完成事件
          ????}?

          //之后分析下callRunners(context, applicationArguments);//調(diào)用ApplicationRunner和CommandLineRunner的方法

          ?

          private?void?callRunners(ApplicationContext?context,?ApplicationArguments?args)?{
          ????????List?runners?=?new?ArrayList<>();
          ????????runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
          ????????runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
          ????????AnnotationAwareOrderComparator.sort(runners);
          ????????for?(Object?runner?:?new?LinkedHashSet<>(runners))?{??//從容器中獲取ApplicationRunner和CommandLineRunner接口的實(shí)現(xiàn)類,并調(diào)用對(duì)應(yīng)的方法,如果我們需要在項(xiàng)目啟動(dòng)完畢后做一些事情,如讀取一些配置信息,可以在實(shí)現(xiàn)這2個(gè)接口
          ????????????if?(runner?instanceof?ApplicationRunner)?{
          ????????????????callRunner((ApplicationRunner)?runner,?args);
          ????????????}
          ????????????if?(runner?instanceof?CommandLineRunner)?{
          ????????????????callRunner((CommandLineRunner)?runner,?args);
          ????????????}
          ????????}
          ????}

          ?最后講講springboot自動(dòng)裝配機(jī)制:前面 load(context, sources.toArray(new Object[0])); 我們分析過(guò),它會(huì)加載主啟動(dòng)類,而主啟動(dòng)類有個(gè)注解@SpringBootApplication,該注解會(huì)被解析,所以我們分析下該注解:

          ?這里有個(gè)重要的注解:

          ?該注解向容器注入了一個(gè)bean,@import可以把其當(dāng)作@componet注解,一樣是往容器中注入某個(gè)bean,那么我們可以分析該bean:

          ?AutoConfigurationImportSelector 實(shí)現(xiàn)了ImportSelector 接口,該接口有個(gè)方法返回需要注入spring容器的bean的全限定類名:

          ?現(xiàn)在我們只要分析實(shí)現(xiàn)類的這個(gè)方法即可:

          @Override
          ????public?String[]?selectImports(AnnotationMetadata?annotationMetadata)?{
          ????????if?(!isEnabled(annotationMetadata))?{
          ????????????return?NO_IMPORTS;
          ????????}
          ????????AutoConfigurationEntry?autoConfigurationEntry?=?getAutoConfigurationEntry(annotationMetadata);?//該方法是重點(diǎn)
          ????????return?StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());//將獲取到的配置類轉(zhuǎn)成數(shù)組
          ????}
          protected?AutoConfigurationEntry?getAutoConfigurationEntry(AnnotationMetadata?annotationMetadata)?{
          ????????if?(!isEnabled(annotationMetadata))?{
          ????????????return?EMPTY_ENTRY;
          ????????}
          ????????AnnotationAttributes?attributes?=?getAttributes(annotationMetadata);?//獲取@EnableAutoConfiguration?注解的屬性exclude?和?excludeName
          ????????List?configurations?=?getCandidateConfigurations(annotationMetadata,?attributes);//獲取配置類,這個(gè)是重點(diǎn)后續(xù)跟進(jìn)
          ????????configurations?=?removeDuplicates(configurations);//去重,下面是做一些排除操作,因?yàn)锧EnableAutoConfiguration?注解可以配置要排除哪些類
          ????????Set?exclusions?=?getExclusions(annotationMetadata,?attributes);
          ????????checkExcludedClasses(configurations,?exclusions);
          ????????configurations.removeAll(exclusions);
          ????????configurations?=?getConfigurationClassFilter().filter(configurations);
          ????????fireAutoConfigurationImportEvents(configurations,?exclusions);
          ????????return?new?AutoConfigurationEntry(configurations,?exclusions);
          ????}

          ?如上圖所示:這里的意思是加載spring.factories文件中key為 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有value值,注意的是這些value值并不要求是EnableAutoConfiguration的子類,他們可以沒(méi)有任何繼承關(guān)系

          這些value值的bean都會(huì)被spring管理,這也就是各種框架整合springBoot的核心所在,因?yàn)槟愕庙?xiàng)目如果想交給spring管理,你可以將自己的配置類配到spring.factories文件中,key為org.springframework.boot.autoconfigure.EnableAutoConfiguration 即可






          ??? ?



          感謝點(diǎn)贊支持下哈?

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    亚洲国产精品VA在线看黑人 | 久久午夜无码人妻精品蜜桃冫 | 天天插天天日天天干 | 中文字幕在线乱 | 十八禁91|