1. @Configuration 類(lèi)型的 class 需要知道的細(xì)節(jié)

        共 5321字,需瀏覽 11分鐘

         ·

        2021-12-31 14:56

        點(diǎn)擊上方“程序員大白”,選擇“星標(biāo)”公眾號(hào)

        重磅干貨,第一時(shí)間送達(dá)

        知識(shí)點(diǎn)收獲

        通過(guò)本文,你能收獲什么?

        • @Configuration.class文件如何被spring加載的?

        • @Configuration.class文件如何被轉(zhuǎn)化為BeanDefinition放入spring容器的?

        • @Configuration的BeanDefinition如何轉(zhuǎn)化為ConfigurationClass對(duì)象的

        • ConfigurationClass對(duì)象屬性在哪里開(kāi)始被解析的?

        • @Configuration的BeanDefinition的beanClass值何時(shí)變成proxy代理類(lèi)的,為什么要變

        • @Configuration類(lèi)最終會(huì)生成cglib proxy代理類(lèi),這個(gè)由@Configuration類(lèi)生成的cglib proxy代理類(lèi)如何實(shí)例化的

        • 我們能從中得到的擴(kuò)展點(diǎn)有哪些

        @Configuration注解作用

        @Configuration標(biāo)識(shí)的類(lèi)有這些特性:可以聲明多個(gè)@Bean方法,且在運(yùn)行時(shí)被spring容器處理來(lái)生成BeanDefinition。@Configuration類(lèi)是被AnnotationConfigWebApplicationContext啟動(dòng)(bootstrap)處理流程的。聲明方式如下代碼

        @Configuration
        ?public?class?AppConfig?{
        ????@Bean
        ????public?MyBean?myBean()?{
        ????????//?instantiate,?configure?and?return?bean?...
        ????}
        }

        @Configuration class存在如下約束,如下圖

        @Configuration class源碼加載分析

        @Configuration相關(guān)的類(lèi)

        如果IDE你使用的Idea,double Shift鍵,輸入ConfigurationClass,會(huì)出現(xiàn)一長(zhǎng)列的類(lèi),

        這些類(lèi)分工協(xié)作,從加載標(biāo)識(shí)@Configuration的.class文件,到生成BeanDefinition,再到生成ConfigurationClass對(duì)象,再到解析ConfigurationClass對(duì)象,即解析@ConfigurationClass class類(lèi)中@Bean、@Import、@ImportResource等注解

        ConfigurationClass:?專(zhuān)門(mén)表示@Configuration class。存儲(chǔ)解析@Configuration class的信息

        ConfigurationClassPostProcessor:?專(zhuān)門(mén)用于處理@Configuration class的BeanDefinitoinRegistryPostProcessor(BeanFactoryPostProcessor子類(lèi))

        AnnotationConfigUtils?:專(zhuān)門(mén)注冊(cè)BeanPostProcessor和BeanFactoryPostProcessor到spring容器

        @Configuration解析過(guò)程

        ConfigurationClassPostProcessor實(shí)例創(chuàng)建過(guò)程

        我們剛說(shuō)過(guò),ConfigurationClassPostProcessor是處理@Configuration class的核心組件,它是BeanFactoryPostProcessor類(lèi)型子類(lèi)且是BeanDefinitoinRegistryPostProcessor類(lèi)型子類(lèi)。BeanFactoryPostProcessor是AbstractApplicationContext's post-processor handling技術(shù)的規(guī)范接口,在項(xiàng)目啟動(dòng)較早時(shí)段,它便開(kāi)始工作?;蛘哒f(shuō)任何spring boot項(xiàng)目啟動(dòng)時(shí)都是走post-processor handling處理邏輯,這個(gè)邏輯的入口在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()方法,代碼如下

        PostProcessorRegistrationDelegate?class
        public?static?void?invokeBeanFactoryPostProcessors(
        ????????ConfigurableListableBeanFactory?beanFactory,?List<BeanFactoryPostProcessor>?beanFactoryPostProcessors)?
        {
        ????···
        ????//?Invoke?BeanDefinitionRegistryPostProcessors?first,?if?any.
        ????if?(beanFactory?instanceof?BeanDefinitionRegistry)?{
        ????????BeanDefinitionRegistry?registry?=?(BeanDefinitionRegistry)?beanFactory;
        ????????List?currentRegistryProcessors?=?new?ArrayList<>();

        ????????//?First,?invoke?the?BeanDefinitionRegistryPostProcessors?that?implement?PriorityOrdered.
        ????????String[]?postProcessorNames?=
        ????????????????beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class,?true,?false);?//?(1)
        ????????for?(String?ppName?:?postProcessorNames)?{
        ????????????if?(beanFactory.isTypeMatch(ppName,?PriorityOrdered.class))?{
        ????????????????currentRegistryProcessors.add(beanFactory.getBean(ppName,?BeanDefinitionRegistryPostProcessor.class));?//?(2)
        ????????????}
        ????????}
        ????????sortPostProcessors(currentRegistryProcessors,?beanFactory);
        ????????//?將@Configuration類(lèi)生成BeanDefinition,此時(shí)的BeanDefinition.beanClass為原類(lèi)class對(duì)象
        ????????invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,?registry);?//?(3)

        ????????//?Now,?invoke?the?postProcessBeanFactory?callback?of?all?processors?handled?so?far.
        ????????//?將(3)處的BeanDefinition.beanClass賦值為proxy代理類(lèi)
        ????????nvokeBeanFactoryPostProcessors(registryProcessors,?beanFactory);(4)
        ????}
        ????···
        }

        這個(gè)方法處理所有的BeanFactoryPostProcessor類(lèi)型對(duì)象,但是通過(guò)優(yōu)先級(jí)來(lái)規(guī)定先后處理順序,優(yōu)先級(jí)維度有兩個(gè):1:BeanDefinitionRegistryPostProcessor類(lèi)型和BeanFactoryPostProcessor類(lèi)型;2:PriorityOrdered和Ordered。而
        ConfigurationClassPostProcessor同時(shí)實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor和PriorityOrdered,所以它得到最優(yōu)先處理的機(jī)會(huì)

        如上方法(1)處,從spring容器獲取BeanDefinitionRegistryPostProcessor類(lèi)型的beanName, 這個(gè)時(shí)期只有ConfigurationClassPostProcessor的beanName滿足,需要指出:ConfigurationClassPostProcessor是通過(guò)硬編碼的方式注冊(cè)到spring容器的,詳見(jiàn)AnnotationConfigUtils類(lèi)),接著執(zhí)行如上方法(2)處,以beanName為key從spring容器中獲取ConfigurationClassPostProcessor實(shí)例。

        應(yīng)用ConfigurationClassPostProcessor實(shí)例加載@Configuration進(jìn)行BeanDefinition注冊(cè)

        如上方法(3)處,即為應(yīng)用ConfigurationClassPostProcessor實(shí)例。

        PostProcessorRegistrationDelegate?class
        private?static?void?invokeBeanDefinitionRegistryPostProcessors(
        ????????Collection?postProcessors,?BeanDefinitionRegistry?registry)?{
        ????for?(BeanDefinitionRegistryPostProcessor?postProcessor?:?postProcessors)?{
        ????????postProcessor.postProcessBeanDefinitionRegistry(registry);
        ????}
        }

        ConfigurationClassPostProcessor?class
        @Override
        public?void?postProcessBeanDefinitionRegistry(BeanDefinitionRegistry?registry)?{
        ????processConfigBeanDefinitions(registry);
        }

        從方法名大概就知道方法的作用了,會(huì)調(diào)用所有BeanDefinitionRegistryPostProcessor類(lèi)型postProcessBeanDefinitionRegistry()方法,此處只有ConfigurationClassPostProcessor符合。

        應(yīng)用ConfigurationClassPostProcessor實(shí)例將BeanDefinition.beanClass生成proxy代理類(lèi)

        如上invokeBeanFactoryPostProcessors方法(4)處,經(jīng)過(guò)了方法(3)的處理,@Configuration類(lèi)已經(jīng)生成BeanDefinition,此時(shí)BeanDefinition.beanClass值為原類(lèi)class對(duì)象。而方法(4)處目的是將BeanDefinition.beanClass賦值為proxy代理類(lèi),這里留個(gè)問(wèn)題,其他為什么要設(shè)置成proxy代理類(lèi)呢

        關(guān)于方法(3)和方法(4)要做的事,參考下圖,時(shí)機(jī)和作用清晰的對(duì)比和展示

        @Configuration class過(guò)程解析

        下面的方法就是解析@Configuration class的核心邏輯了。解析過(guò)程可以總結(jié)分三步,正好對(duì)應(yīng)著方法中(1),(2),(3)處

        ConfigurationClassPostProcessor?class
        public?void?processConfigBeanDefinitions(BeanDefinitionRegistry?registry)?
        {
        ????List?configCandidates?=?new?ArrayList<>();
        ????String[]?candidateNames?=?registry.getBeanDefinitionNames();?

        ????for?(String?beanName?:?candidateNames)?{
        ????????BeanDefinition?beanDef?=?registry.getBeanDefinition(beanName);
        ????????if?(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,?this.metadataReaderFactory)){
        ????????????configCandidates.add(new?BeanDefinitionHolder(beanDef,?beanName));//(1)
        ????????}
        ????}
        ????···?排序等
        s
        ????//?Parse?each?@Configuration?class
        ????ConfigurationClassParser?parser?=?new?ConfigurationClassParser(
        ????????????this.metadataReaderFactory,?this.problemReporter,?this.environment,
        ????????????this.resourceLoader,?this.componentScanBeanNameGenerator,?registry);

        ????Set?candidates?=?new?LinkedHashSet<>(configCandidates);
        ????Set?alreadyParsed?=?new?HashSet<>(configCandidates.size());
        ????do?{
        ????????parser.parse(candidates);?//(2)
        ????????parser.validate();

        ????????Set?configClasses?=?new?LinkedHashSet<>(parser.getConfigurationClasses());

        ????????//?Read?the?model?and?create?bean?definitions?based?on?its?content
        ????????this.reader?=?new?ConfigurationClassBeanDefinitionReader(
        ????????????????registry,?this.sourceExtractor,?this.resourceLoader,?this.environment,
        ????????????????this.importBeanNameGenerator,?parser.getImportRegistry());

        ????????this.reader.loadBeanDefinitions(configClasses);?//(3)

        ????}
        ????while?(!candidates.isEmpty());

        ????···?···
        }

        step1: 獲取候選者
        從spring容器拿到所有的beanDefinitionNames,然后遍歷驗(yàn)證獲得候選者,驗(yàn)證的依據(jù)是class metadata是否含有@Configuration注解,從下面我們可知,此時(shí),beanDefinitionNames中只有
        consumerFeignApp符合條件。所以候選者就是consumerFeignApp及他的beanDefinition

        step2: 通過(guò)候選者獲取ConfigurationClass
        找到了候選者,下面就對(duì)候選者進(jìn)行解析,解析的全部功能和邏輯都集中在ConfigurationClassParser類(lèi)中,看名稱(chēng)可知,這個(gè)類(lèi)專(zhuān)業(yè)解析@Configuration類(lèi)。

        ConfigurationClassParser?class
        public?void?parse(Set?configCandidates)?
        {
        ????for?(BeanDefinitionHolder?holder?:?configCandidates)?{
        ????????BeanDefinition?bd?=?holder.getBeanDefinition();
        ????????if?(bd?instanceof?AnnotatedBeanDefinition)?{
        ????????????parse(((AnnotatedBeanDefinition)?bd).getMetadata(),?holder.getBeanName());
        ????????}
        ????????...
        ????}

        ????this.deferredImportSelectorHandler.process();
        }

        protected?final?void?parse(AnnotationMetadata?metadata,?String?beanName)?throws?IOException?{
        ????processConfigurationClass(new?ConfigurationClass(metadata,?beanName));
        }

        protected?void?processConfigurationClass(ConfigurationClass?configClass)?throws?IOException?{
        ????//?評(píng)估標(biāo)識(shí)@Configuration的類(lèi)是否滿足條件去加載,這是條件注解@ConditionalXXX起的作用
        ????//?實(shí)際開(kāi)發(fā)中,我們可以依據(jù)這個(gè)功能實(shí)現(xiàn)靈活的加載配置(如讓誰(shuí)加載進(jìn)來(lái),不讓誰(shuí)加載進(jìn)來(lái)^_^)
        ????if?(this.conditionEvaluator.shouldSkip(configClass.getMetadata(),?ConfigurationPhase.PARSE_CONFIGURATION))?{
        ????????return;
        ????}

        ????//?Recursively?process?the?configuration?class?and?its?superclass?hierarchy.
        ????SourceClass?sourceClass?=?asSourceClass(configClass);
        ????do?{
        ????????sourceClass?=?doProcessConfigurationClass(configClass,?sourceClass);
        ????}
        ????while?(sourceClass?!=?null);

        ????//?所有加載的@Configuration類(lèi)都會(huì)轉(zhuǎn)為ConfigurationClass放入這個(gè)map中
        ????this.configurationClasses.put(configClass,?configClass);
        }

        protected?final?SourceClass?doProcessConfigurationClass(ConfigurationClass?configClass,?SourceClass?sourceClass){
        ????//?為了集中說(shuō)明意圖,隱藏了代碼
        ????//?Recursively?process?any?member?(nested)?classes?first
        ????//?Process?any?@PropertySource?annotations
        ????//?Process?any?@ComponentScan?annotations
        ????for?(AnnotationAttributes?componentScan?:?componentScans)?{
        ????????for?(BeanDefinitionHolder?holder?:?scannedBeanDefinitions)?{
        ????????????if?()?{
        ????????????????//?進(jìn)入遞歸調(diào)用
        ????????????????parse(bdCand.getBeanClassName(),?holder.getBeanName());
        ????????????}
        ????????}
        ????}

        ????//?Process?any?@Import?annotations
        ????//?Process?any?@ImportResource?annotations
        ????//?Process?individual?@Bean?methods
        ????//?Process?default?methods?on?interfaces
        ????//?Process?superclass,?if?any
        ????//?No?superclass?->?processing?is?complete
        ????return?null;
        }

        如上方法整體的邏輯為對(duì)ConfigurationClass和SourceClass解析,檢查他們有沒(méi)有@ComponentScan,@Import,@Bean methods,@ImportResource,@PropertySource這些注解,如果有,分別對(duì)其解析,解析后的結(jié)果放入ConfigurationClass的各屬性中

        各個(gè)注解的屬性值中可能又包含@Configuration注解,又要對(duì)包含的@Configuration注解進(jìn)行解析,這樣形成了遞歸,所以解析過(guò)程中有三個(gè)方法形成了三角遞歸調(diào)用的邏輯,

        這一步會(huì)將我們項(xiàng)目中定義的@Configuration類(lèi)都加載進(jìn)來(lái),你可能有疑問(wèn),難道項(xiàng)目中我們自己定義的@Configuration類(lèi)都是靠遞歸加載進(jìn)來(lái)的?答案當(dāng)然是NO,請(qǐng)注意@ComponentScan注解,這個(gè)注解的解析器很厲害,它把所有的標(biāo)識(shí)@Component注解的class加載進(jìn)來(lái),而@Configuration,@RestController,@Service,@Repository等都包含@Component,所有這些注解的class都會(huì)加載進(jìn)來(lái)形成BeanDefinition存入spring 容器(解析過(guò)程詳見(jiàn)ComponentScanAnnotationParser)。說(shuō)回來(lái),對(duì)于@ComponentScan解析器加載進(jìn)來(lái)的BeanDefinitoin,會(huì)進(jìn)行時(shí)@Configuration進(jìn)行過(guò)濾,從而得到@Configuration類(lèi),再次調(diào)用parse()方法,這時(shí)體現(xiàn)出三角遞歸調(diào)用了。此時(shí),項(xiàng)目中所有我們自定義的@Configuration類(lèi)都獲取到了

        step3: 解析每個(gè)ConfigurationClass
        step2中對(duì)@Configuration類(lèi)的@Import,@Bean methods,@ImportResource進(jìn)行解析,解析的結(jié)果放入ConfigurationClass對(duì)象的importBeanDefinitionRegistrars,beanMethods,importedResources,metadata等屬性。
        所以,step2將@Configuration類(lèi)的解析結(jié)果都放入了ConfigurationClass對(duì)象,即ConfigurationClass對(duì)象包裝了@Configuration類(lèi)的所有信息。

        回到ConfigurationClassPostProcessor.processConfigBeanDefinitions()方法(3)處,現(xiàn)在,我們解析ConfigurationClass,而解析ConfigurationClass過(guò)程由ConfigurationClassBeanDefinitionReader類(lèi)負(fù)責(zé)的
        看code

        ConfigurationClassBeanDefinitionReader?class
        public?void?loadBeanDefinitions(Set?configurationModel)?{
        ????TrackedConditionEvaluator?trackedConditionEvaluator?=?new?TrackedConditionEvaluator();
        ????for?(ConfigurationClass?configClass?:?configurationModel)?{
        ????????loadBeanDefinitionsForConfigurationClass(configClass,?trackedConditionEvaluator);
        ????}
        }

        private?void?loadBeanDefinitionsForConfigurationClass(
        ????????ConfigurationClass?configClass,?TrackedConditionEvaluator?trackedConditionEvaluator)?{

        ????if?(trackedConditionEvaluator.shouldSkip(configClass))?{
        ????????String?beanName?=?configClass.getBeanName();
        ????????if?(StringUtils.hasLength(beanName)?&&?this.registry.containsBeanDefinition(beanName))?{
        ????????????this.registry.removeBeanDefinition(beanName);
        ????????}
        ????????this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        ????????return;
        ????}

        ????if?(configClass.isImported())?{
        ????????registerBeanDefinitionForImportedConfigurationClass(configClass);
        ????}
        ????for?(BeanMethod?beanMethod?:?configClass.getBeanMethods())?{
        ????????loadBeanDefinitionsForBeanMethod(beanMethod);
        ????}

        ????loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        ????loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
        }

        看loadBeanDefinitionsForConfigurationClass()方法,方法主要功能為對(duì)ConfigurationClass的beanMethods,importedResources,importBeanDefinitionReistrars屬性進(jìn)行解析,為什么要對(duì)這三個(gè)屬性進(jìn)行解析呢,看看這三個(gè)其@Import,@Bean methods,@ImportResource的用法

        @Import(DispatcherServletConfiguration.class)
        @ImportResource("classpath:/com/acme/database-config.xml")
        protected?static?class?DispatcherServletRegistrationConfiguration?{

        ????@Bean(name?=?DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        ????public?DispatcherServletRegistrationBean?dispatcherServletRegistration(){
        ????????...
        ????}
        }

        可以看到,這三個(gè)注解的屬性值都是類(lèi)或者配置文件或者加載文件的類(lèi),所以,需要解析,從而將解析到的.class文件轉(zhuǎn)化為BeanDefinition放入spring容器。

        @Configuration類(lèi)的cglib代理類(lèi)實(shí)例化分析

        由于@Configuration注解的都是類(lèi),而非接口,所有這里使用的是cglib代理技術(shù),ConfigurationClassEnhancer包裝了cglib。這里我們實(shí)際工作中可以直接復(fù)用ConfigurationClassEnhancer滿足我們生成代理類(lèi)的場(chǎng)景

        實(shí)現(xiàn)自BeanFactoryPostProcessor.postProcessBeanFactory
        ConfigurationClassPostProcessor?class
        @Override
        public?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)?{
        ????//?生成代理
        ????enhanceConfigurationClasses(beanFactory);
        ????beanFactory.addBeanPostProcessor(new?ImportAwareBeanPostProcessor(beanFactory));
        }

        ConfigurationClassPostProcessor.ConfigurationClassEnhancer?class
        public?void?enhanceConfigurationClasses(ConfigurableListableBeanFactory?beanFactory)?{
        ????Map?configBeanDefs?=?new?LinkedHashMap<>();
        ????for?(String?beanName?:?beanFactory.getBeanDefinitionNames())?{
        ????????BeanDefinition?beanDef?=?beanFactory.getBeanDefinition(beanName);
        ????????if?(ConfigurationClassUtils.isFullConfigurationClass(beanDef))?{
        ????????????if?(!(beanDef?instanceof?AbstractBeanDefinition))?{
        ????????????????throw?
        ????????????}
        ????????????configBeanDefs.put(beanName,?(AbstractBeanDefinition)?beanDef);
        ????????}
        ????}

        ????ConfigurationClassEnhancer?enhancer?=?new?ConfigurationClassEnhancer();
        ????for?(Map.Entry?entry?:?configBeanDefs.entrySet())?{
        ????????AbstractBeanDefinition?beanDef?=?entry.getValue();
        ????????//?If?a?@Configuration?class?gets?proxied,?always?proxy?the?target?class
        ????????beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE,?Boolean.TRUE);
        ????????//?Set?enhanced?subclass?of?the?user-specified?bean?class
        ????????Class?configClass?=?beanDef.resolveBeanClass(this.beanClassLoader);
        ????????if?(configClass?!=?null)?{
        ????????????Class?enhancedClass?=?enhancer.enhance(configClass,?this.beanClassLoader);
        ????????????if?(configClass?!=?enhancedClass)?{
        ????????????????beanDef.setBeanClass(enhancedClass);
        ????????????}
        ????????}
        ????}
        }

        ConfigurationClassPostProcessor.ConfigurationClassEnhancer?class
        public?Class?enhance(Class?configClass,?@Nullable?ClassLoader?classLoader)?{
        ????if?(EnhancedConfiguration.class.isAssignableFrom(configClass))?{
        ????????return?configClass;
        ????}
        ????Class?enhancedClass?=?createClass(newEnhancer(configClass,?classLoader));
        ????return?enhancedClass;
        }

        代理的代碼很清晰,很值得我們學(xué)習(xí)

        it`s time to summary

        整個(gè)過(guò)程可以看做是一顆小樹(shù)長(zhǎng)成參天大樹(shù),consumerFeignApp就是樹(shù)苗,而我們項(xiàng)目的代碼就是后來(lái)大樹(shù)的枝干和葉子。枝干和葉子與@ComponentScan,@Import,@Bean methods,@ImportResource,@PropertySource,@Configuration交織在一起被解析出來(lái),生成beanDefinition、實(shí)例對(duì)象或代理類(lèi)。

        本文主要說(shuō)明了標(biāo)識(shí)了@Configuration的.class文件,是如何被解析成ConfigurationClass,再到轉(zhuǎn)化為ConfigurationClassBeanDefinition放入spring容器,再到如何解析ConfigurationClass對(duì)象屬性。為了集中闡述@Configuration,所以,其他的部分這里不做詳述和延展。如果閱讀后你有所收獲,共享歡喜

        知識(shí)點(diǎn)收獲回復(fù)

        1,2,3,4相信你已經(jīng)有答案了,現(xiàn)在,我們看看擴(kuò)展點(diǎn)有什么

        1. 加載一些package目錄下的.class文件

        ClassPathBeanDefinitionScanner?scanner?=?new?ClassPathBeanDefinitionScanner(this.registry,
        ????????????componentScan.getBoolean("useDefaultFilters"),?this.environment,?this.resourceLoader);
        Set?beanDefinitions?=?scanner.doScan(StringUtils.toStringArray(basePackages));
        如此兩行代碼就實(shí)現(xiàn)了加載package目錄下的.class文件的功能。詳見(jiàn)ComponentScanAnnotationParser.parse()方法


        source:?//yaoyuanyy.github.io/2019/12/12/springboot%20@Configuration類(lèi)型的class需要知道的細(xì)節(jié)



        13個(gè)你一定要知道的PyTorch特性

        解讀:為什么要做特征歸一化/標(biāo)準(zhǔn)化?

        一文搞懂 PyTorch 內(nèi)部機(jī)制

        張一鳴:每個(gè)逆襲的年輕人,都具備的底層能力


        關(guān)


        學(xué),西學(xué)學(xué)運(yùn)營(yíng)護(hù)號(hào),樂(lè)質(zhì),結(jié)識(shí),關(guān)[]學(xué)習(xí)進(jìn)!


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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 欧美一级特黄aaaaaa在线看片 | 国产麻豆自拍 | www.欧美日韩一级在线 | 国产精品久久AV | 国产精品igao视频网网址男男 |