日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Boot知识清单

發(fā)布時間:2024/1/17 javascript 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Boot知识清单 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

?Spring Boot知識清單

?

Spring Boot 應(yīng)用本質(zhì)上就是一個基于 Spring 框架的應(yīng)用,它是 Spring 對“約定優(yōu)先于配置”理念的最佳實踐產(chǎn)物,它能夠幫助開發(fā)者更快速高效地構(gòu)建基于 Spring 生態(tài)圈的應(yīng)用。

那 Spring Boot 有何魔法?自動配置、起步依賴、Actuator、命令行界面(CLI) 是 Spring Boot 最重要的 4 大核心特性。

其中 CLI 是 Spring Boot 的可選特性,雖然它功能強(qiáng)大,但也引入了一套不太常規(guī)的開發(fā)模型,因而這個系列的文章僅關(guān)注其他 3 種特性。

本文將為你打開 Spring Boot 的大門,重點為你剖析其啟動流程以及自動配置實現(xiàn)原理。要掌握這部分核心內(nèi)容,理解一些 Spring 框架的基礎(chǔ)知識,將會讓你事半功倍。

?

拋磚引玉:探索 Spring IOC 容器

如果有看過 SpringApplication.run() 方法的源碼,Spring Boot 冗長無比的啟動流程一定會讓你抓狂。

透過現(xiàn)象看本質(zhì),SpringApplication 只是將一個典型的Spring應(yīng)用的啟動流程進(jìn)行了擴(kuò)展,因此,透徹理解 Spring 容器是打開 Spring Boot 大門的一把鑰匙。

?

Spring IOC 容器

可以把 Spring IOC 容器比作一間餐館,當(dāng)你來到餐館,通常會直接招呼服務(wù)員:點菜!至于菜的原料是什么?如何用原料把菜做出來?可能你根本就不關(guān)心。

IOC 容器也是一樣,你只需要告訴它需要某個 bean,它就把對應(yīng)的實例(instance)扔給你,至于這個 bean 是否依賴其他組件,怎樣完成它的初始化,根本就不需要你關(guān)心。

作為餐館,想要做出菜肴,得知道菜的原料和菜譜,同樣地,IOC 容器想要管理各個業(yè)務(wù)對象以及它們之間的依賴關(guān)系,需要通過某種途徑來記錄和管理這些信息。

BeanDefinition 對象就承擔(dān)了這個責(zé)任:容器中的每一個 bean 都會有一個對應(yīng)的 BeanDefinition 實例。

該實例負(fù)責(zé)保存 bean 對象的所有必要信息,包括 bean 對象的 class 類型、是否是抽象類、構(gòu)造方法和參數(shù)、其他屬性等等。

當(dāng)客戶端向容器請求相應(yīng)對象時,容器就會通過這些信息為客戶端返回一個完整可用的 bean 實例。

原材料已經(jīng)準(zhǔn)備好(把 BeanDefinition 看做原料),開始做菜吧,等等,你還需要一份菜譜。

BeanDefinitionRegistry 和 BeanFactory 就是這份菜譜,BeanDefinitionRegistry 抽象出 bean 的注冊邏輯。

而 BeanFactory 則抽象出了 bean 的管理邏輯,而各個 BeanFactory 的實現(xiàn)類就具體承擔(dān)了 bean 的注冊以及管理工作。

它們之間的關(guān)系就如下圖:

BeanFactory、BeanDefinitionRegistry 關(guān)系圖(來自:Spring 揭秘)

DefaultListableBeanFactory 作為一個比較通用的 BeanFactory 實現(xiàn),它同時也實現(xiàn)了 BeanDefinitionRegistry 接口,因此它就承擔(dān)了 bean 的注冊管理工作。

從圖中也可以看出,BeanFactory 接口中主要包含 getBean、containBean、getType、getAliases 等管理 bean 的方法。

而 BeanDefinitionRegistry 接口則包含 registerBeanDefinition、removeBeanDefinition、getBeanDefinition 等注冊管理 BeanDefinition 的方法。

下面通過一段簡單的代碼來模擬 BeanFactory 底層是如何工作的:

這段代碼僅為了說明 BeanFactory 底層的大致工作流程,實際情況會更加復(fù)雜。

比如 bean 之間的依賴關(guān)系可能定義在外部配置文件(XML/Properties)中、也可能是注解方式。

Spring IoC 容器的整個工作流程大致可以分為兩個階段:

?

①容器啟動階段

容器啟動時,會通過某種途徑加載 ConfigurationMetaData。除了代碼方式比較直接外,在大部分情況下,容器需要依賴某些工具類。

比如:BeanDefinitionReader,它會對加載的 ConfigurationMetaData 進(jìn)行解析和分析,并將分析后的信息組裝為相應(yīng)的 BeanDefinition。

最后把這些保存了 bean 定義的 BeanDefinition,注冊到相應(yīng)的 BeanDefinitionRegistry,這樣容器的啟動工作就完成了。

這個階段主要完成一些準(zhǔn)備性工作,更側(cè)重于 bean 對象管理信息的收集,當(dāng)然一些驗證性或者輔助性的工作也在這一階段完成。

來看一個簡單的例子吧,過往,所有的 bean 都定義在 XML 配置文件中,下面的代碼將模擬 BeanFactory 如何從配置文件中加載 bean 的定義以及依賴關(guān)系:

?

②Bean 的實例化階段

經(jīng)過第一階段,所有 bean 定義都通過 BeanDefinition 的方式注冊到 BeanDefinitionRegistry 中。

當(dāng)某個請求通過容器的 getBean 方法請求某個對象,或者因為依賴關(guān)系容器需要隱式的調(diào)用 getBean 時,就會觸發(fā)第二階段的活動:容器會首先檢查所請求的對象之前是否已經(jīng)實例化完成。

如果沒有,則會根據(jù)注冊的 BeanDefinition 所提供的信息實例化被請求對象,并為其注入依賴。當(dāng)該對象裝配完畢后,容器會立即將其返回給請求方使用。

BeanFactory 只是 Spring IoC 容器的一種實現(xiàn),如果沒有特殊指定,它采用延遲初始化策略:只有當(dāng)訪問容器中的某個對象時,才對該對象進(jìn)行初始化和依賴注入操作。

而在實際場景下,我們更多的使用另外一種類型的容器: ApplicationContext,它構(gòu)建在 BeanFactory 之上,屬于更高級的容器。

除了具有 BeanFactory 的所有能力之外,還提供對事件監(jiān)聽機(jī)制以及國際化的支持等。它管理的 bean,在容器啟動時全部完成初始化和依賴注入操作。

?

Spring 容器擴(kuò)展機(jī)制

IoC 容器負(fù)責(zé)管理容器中所有 bean 的生命周期,而在 bean 生命周期的不同階段,Spring 提供了不同的擴(kuò)展點來改變 bean 的命運(yùn)。

在容器的啟動階段, BeanFactoryPostProcessor 允許我們在容器實例化相應(yīng)對象之前,對注冊到容器的 BeanDefinition 所保存的信息做一些額外的操作,比如修改bean定義的某些屬性或者增加其他信息等。

如果要自定義擴(kuò)展類,通常需要實現(xiàn) org.springframework.beans.factory.config.BeanFactoryPostProcessor 接口。

與此同時,因為容器中可能有多個 BeanFactoryPostProcessor,可能還需要實現(xiàn) org.springframework.core.Ordered 接口,以保證 BeanFactoryPostProcessor 按照順序執(zhí)行。

Spring 提供了為數(shù)不多的 BeanFactoryPostProcessor 實現(xiàn),我們以 PropertyPlaceholderConfigurer 來說明其大致的工作流程。

在 Spring 項目的 XML 配置文件中,經(jīng)??梢钥吹皆S多配置項的值使用占位符,而將占位符所代表的值單獨配置到獨立的 properties 文件。

這樣可以將散落在不同 XML 文件中的配置集中管理,而且也方便運(yùn)維根據(jù)不同的環(huán)境進(jìn)行配置不同的值。這個非常實用的功能就是由 PropertyPlaceholderConfigurer 負(fù)責(zé)實現(xiàn)的。

根據(jù)前文,當(dāng) BeanFactory 在第一階段加載完所有配置信息時,BeanFactory 中保存的對象的屬性還是以占位符方式存在的,比如 ${jdbc.mysql.url}。

當(dāng) PropertyPlaceholderConfigurer 作為 BeanFactoryPostProcessor 被應(yīng)用時,它會使用 properties 配置文件中的值來替換相應(yīng)的 BeanDefinition 中占位符所表示的屬性值。

當(dāng)需要實例化 bean 時,bean 定義中的屬性值就已經(jīng)被替換成我們配置的值。

當(dāng)然其實現(xiàn)比上面描述的要復(fù)雜一些,這里僅說明其大致工作原理,更詳細(xì)的實現(xiàn)可以參考其源碼。

與之相似的,還有 BeanPostProcessor,其存在于對象實例化階段。跟 BeanFactoryPostProcessor 類似,它會處理容器內(nèi)所有符合條件并且已經(jīng)實例化后的對象。

簡單的對比,BeanFactoryPostProcessor 處理 bean 的定義,而 BeanPostProcessor 則處理 bean 完成實例化后的對象。

BeanPostProcessor 定義了兩個接口:

為了理解這兩個方法執(zhí)行的時機(jī),簡單的了解下 bean 的整個生命周期:

Bean 的實例化過程(來自:Spring 揭秘)

postProcessBeforeInitialization()方法與 postProcessAfterInitialization() 分別對應(yīng)圖中前置處理和后置處理兩個步驟將執(zhí)行的方法。

這兩個方法中都傳入了 bean 對象實例的引用,為擴(kuò)展容器的對象實例化過程提供了很大便利,在這兒幾乎可以對傳入的實例執(zhí)行任何操作。

注解、AOP 等功能的實現(xiàn)均大量使用了 BeanPostProcessor,比如有一個自定義注解,你完全可以實現(xiàn) BeanPostProcessor 的接口,在其中判斷 bean 對象的腦袋上是否有該注解。

如果有,你可以對這個 bean 實例執(zhí)行任何操作,想想是不是非常的簡單?

再來看一個更常見的例子,在 Spring 中經(jīng)常能夠看到各種各樣的 Aware 接口,其作用就是在對象實例化完成以后將 Aware 接口定義中規(guī)定的依賴注入到當(dāng)前實例中。

比如最常見的 ApplicationContextAware 接口,實現(xiàn)了這個接口的類都可以獲取到一個 ApplicationContext 對象。

當(dāng)容器中每個對象的實例化過程走到 BeanPostProcessor 前置處理這一步時,容器會檢測到之前注冊到容器的 ApplicationContextAwareProcessor。

然后就會調(diào)用其 postProcessBeforeInitialization() 方法,檢查并設(shè)置 Aware 相關(guān)依賴。

看看代碼吧,是不是很簡單:

最后總結(jié)一下,本小節(jié)內(nèi)容和你一起回顧了 Spring 容器的部分核心內(nèi)容,限于篇幅不能寫更多,但理解這部分內(nèi)容,足以讓您輕松理解 Spring Boot 的啟動原理。

如果在后續(xù)的學(xué)習(xí)過程中遇到一些晦澀難懂的知識,再回過頭來看看 Spring 的核心知識,也許有意想不到的效果。

也許 Spring Boot 的中文資料很少,但 Spring 的中文資料和書籍有太多太多,總有東西能給你啟發(fā)。

?

夯實基礎(chǔ):JavaConfig 與常見 Annotation

?

JavaConfig

我們知道 bean 是 Spring IOC 中非常核心的概念,Spring 容器負(fù)責(zé) bean 的生命周期的管理。

在最初,Spring 使用 XML 配置文件的方式來描述 bean 的定義以及相互間的依賴關(guān)系,但隨著 Spring 的發(fā)展,越來越多的人對這種方式表示不滿。

因為 Spring 項目的所有業(yè)務(wù)類均以 bean 的形式配置在 XML 文件中,造成了大量的 XML 文件,使項目變得復(fù)雜且難以管理。

后來,基于純 Java Annotation 依賴注入框架 Guice 出世,其性能明顯優(yōu)于采用 XML 方式的 Spring。

甚至有部分人認(rèn)為, Guice 可以完全取代 Spring( Guice 僅是一個輕量級 IOC 框架,取代 Spring 還差的挺遠(yuǎn))。

正是這樣的危機(jī)感,促使 Spring 及社區(qū)推出并持續(xù)完善了 JavaConfig 子項目,它基于 Java 代碼和 Annotation 注解來描述 bean 之間的依賴綁定關(guān)系。

比如,下面是使用 XML 配置方式來描述 bean 的定義:

而基于 JavaConfig 的配置形式是這樣的:

如果兩個 bean 之間有依賴關(guān)系的話,在 XML 配置中應(yīng)該是這樣:

而在 JavaConfig 中則是這樣:

你可能注意到這個示例中,有兩個 bean 都依賴于 dependencyService,也就是說當(dāng)初始化 bookService 時會調(diào)用 dependencyService(),在初始化 otherService 時也會調(diào)用 dependencyService(),那么問題來了?

這時候 IOC 容器中是有一個 dependencyService 實例還是兩個?這個問題留著大家思考吧,這里不再贅述。

?

@ComponentScan

@ComponentScan 注解對應(yīng)XML配置形式中的?元素,表示啟用組件掃描,Spring 會自動掃描所有通過注解配置的 bean,然后將其注冊到 IOC 容器中。

我們可以通過 basePackages 等屬性來指定 @ComponentScan 自動掃描的范圍,如果不指定,默認(rèn)從聲明 @ComponentScan 所在類的 package 進(jìn)行掃描。正因為如此,SpringBoot 的啟動類都默認(rèn)在 src/main/java 下。

?

@Import

@Import 注解用于導(dǎo)入配置類,舉個簡單的例子:

現(xiàn)在有另外一個配置類,比如:MoonUserConfiguration,這個配置類中有一個 bean 依賴于 MoonBookConfiguration 中的 bookService,如何將這兩個 bean 組合在一起?

借助 @Import 即可:

需要注意的是,在 4.2 之前, @Import 注解只支持導(dǎo)入配置類,但是在 4.2 之后,它支持導(dǎo)入普通類,并將這個類作為一個 bean 的定義注冊到 IOC 容器中。

?

@Conditional

@Conditional 注解表示在滿足某種條件后才初始化一個 bean 或者啟用某些配置。

它一般用在由 @Component、@Service、@Configuration 等注解標(biāo)識的類上面,或者由 @Bean 標(biāo)記的方法上。

如果一個 @Configuration 類標(biāo)記了 @Conditional,則該類中所有標(biāo)識了 @Bean 的方法和 @Import 注解導(dǎo)入的相關(guān)類將遵從這些條件。

在 Spring 里可以很方便的編寫你自己的條件類,所要做的就是實現(xiàn) Condition 接口,并覆蓋它的 matches()方法。

舉個例子,下面的簡單條件類表示只有在 Classpath 里存在 JdbcTemplate 類時才生效:

當(dāng)你用 Java 來聲明 bean 的時候,可以使用這個自定義條件類:

這個例子中只有當(dāng) JdbcTemplateCondition 類的條件成立時才會創(chuàng)建 MyService 這個 bean。

也就是說 MyService 這 bean 的創(chuàng)建條件是 classpath 里面包含 JdbcTemplate,否則這個 bean 的聲明就會被忽略掉。

Spring Boot 定義了很多有趣的條件,并把他們運(yùn)用到了配置類上,這些配置類構(gòu)成了 Spring Boot 的自動配置的基礎(chǔ)。

Spring Boot 運(yùn)用條件化配置的方法是:定義多個特殊的條件化注解,并將它們用到配置類上。

下面列出了 Spring Boot 提供的部分條件化注解:

?

@ConfigurationProperties與@EnableConfigurationProperties

當(dāng)某些屬性的值需要配置的時候,我們一般會在 application.properties 文件中新建配置項,然后在 bean 中使用 @Value 注解來獲取配置的值。

比如下面配置數(shù)據(jù)源的代碼:

使用 @Value 注解注入的屬性通常都比較簡單,如果同一個配置在多個地方使用,也存在不方便維護(hù)的問題(考慮下,如果有幾十個地方在使用某個配置,而現(xiàn)在你想改下名字,你該怎么做?)。

對于更為復(fù)雜的配置,Spring Boot 提供了更優(yōu)雅的實現(xiàn)方式,那就是 @ConfigurationProperties 注解。

我們可以通過下面的方式來改寫上面的代碼:

@ConfigurationProperties 對于更為復(fù)雜的配置,處理起來也是得心應(yīng)手,比如有如下配置文件:

可以定義如下配置類來接收這些屬性:

@EnableConfigurationProperties 注解表示對 @ConfigurationProperties 的內(nèi)嵌支持,默認(rèn)會將對應(yīng) Properties Class 作為 bean 注入的 IOC 容器中,即在相應(yīng)的 Properties 類上不用加 @Component 注解。

?

削鐵如泥:SpringFactoriesLoader 詳解

JVM 提供了 3 種類加載器: BootstrapClassLoader、 ExtClassLoader、 AppClassLoader 分別加載 Java 核心類庫、擴(kuò)展類庫以及應(yīng)用的類路徑( CLASSPATH)下的類庫。

JVM 通過雙親委派模型進(jìn)行類的加載,我們也可以通過繼承 java.lang.classloader 實現(xiàn)自己的類加載器。

何為雙親委派模型?當(dāng)一個類加載器收到類加載任務(wù)時,會先交給自己的父加載器去完成。

因此最終加載任務(wù)都會傳遞到最頂層的 BootstrapClassLoader,只有當(dāng)父加載器無法完成加載任務(wù)時,才會嘗試自己來加載。

采用雙親委派模型的一個好處是保證使用不同類加載器最終得到的都是同一個對象,這樣就可以保證 Java 核心庫的類型安全。

比如,加載位于 rt.jar 包中的 java.lang.Object 類,不管是哪個加載器加載這個類,最終都是委托給頂層的 BootstrapClassLoader 來加載的,這樣就可以保證任何的類加載器最終得到的都是同樣一個 Object 對象。

查看 ClassLoader 的源碼,對雙親委派模型會有更直觀的認(rèn)識:

但雙親委派模型并不能解決所有的類加載器問題,比如,Java 提供了很多服務(wù)提供者接口( ServiceProviderInterface,SPI),允許第三方為這些接口提供實現(xiàn)。

常見的 SPI 有 JDBC、JNDI、JAXP 等,這些 SPI 的接口由核心類庫提供,卻由第三方實現(xiàn)。

這樣就存在一個問題:SPI 的接口是 Java 核心庫的一部分,是由 BootstrapClassLoader 加載的;SPI 實現(xiàn)的 Java 類一般是由 AppClassLoader 來加載的。

BootstrapClassLoader 是無法找到 SPI 的實現(xiàn)類的,因為它只加載 Java 的核心庫。

它也不能代理給 AppClassLoader,因為它是最頂層的類加載器。也就是說,雙親委派模型并不能解決這個問題。

線程上下文類加載器( ContextClassLoader)正好解決了這個問題。從名稱上看,可能會誤解為它是一種新的類加載器,實際上,它僅僅是 Thread 類的一個變量而已。

可以通過 setContextClassLoader(ClassLoadercl)和 getContextClassLoader()來設(shè)置和獲取該對象。

如果不做任何的設(shè)置,Java 應(yīng)用的線程的上下文類加載器默認(rèn)就是 AppClassLoader。

在核心類庫使用 SPI 接口時,傳遞的類加載器使用線程上下文類加載器,就可以成功的加載到 SPI 實現(xiàn)的類。

線程上下文類加載器在很多 SPI 的實現(xiàn)中都會用到。但在 JDBC 中,你可能會看到一種更直接的實現(xiàn)方式。

比如,JDBC 驅(qū)動管理 java.sql.Driver 中的 loadInitialDrivers() 方法中,你可以直接看到 JDK 是如何加載驅(qū)動的:

其實講解線程上下文類加載器,最主要是讓大家在看到 Thread.currentThread().getClassLoader()和Thread.currentThread().getContextClassLoader()時不會一臉懵逼。

這兩者除了在許多底層框架中取得的 ClassLoader 可能會有所不同外,其他大多數(shù)業(yè)務(wù)場景下都是一樣的,大家只要知道它是為了解決什么問題而存在的即可。

類加載器除了加載 class 外,還有一個非常重要功能,就是加載資源,它可以從 jar 包中讀取任何資源文件。

比如, ClassLoader.getResources(Stringname) 方法就是用于讀取 jar 包中的資源文件,其代碼如下:

是不是覺得有點眼熟,沒錯,它的邏輯其實跟類加載的邏輯是一樣的,首先判斷父類加載器是否為空,不為空則委托父類加載器執(zhí)行資源查找任務(wù),直到 BootstrapClassLoader,最后才輪到自己查找。

而不同的類加載器負(fù)責(zé)掃描不同路徑下的 jar 包,就如同加載 class 一樣,最后會掃描所有的 jar 包,找到符合條件的資源文件。

類加載器的 findResources(name) 方法會遍歷其負(fù)責(zé)加載的所有 jar 包,找到 jar 包中名稱為 name 的資源文件,這里的資源可以是任何文件,甚至是 .class 文件。

比如下面的示例,用于查找 Array.class 文件:

運(yùn)行后可以得到如下結(jié)果:

根據(jù)資源文件的 URL,可以構(gòu)造相應(yīng)的文件來讀取資源內(nèi)容。

看到這里,你可能會感到挺奇怪的,你不是要詳解 SpringFactoriesLoader 嗎?上來講了一堆 ClassLoader 是幾個意思?

看下它的源碼你就知道了:

有了前面關(guān)于 ClassLoader 的知識,再來理解這段代碼,是不是感覺豁然開朗:從 CLASSPATH 下的每個 jar 包中搜尋所有 META-INF/spring.factories 配置文件,然后將解析 properties 文件,找到指定名稱的配置后返回。

需要注意的是,其實這里不僅僅是會去 ClassPath 路徑下查找,會掃描所有路徑下的 jar 包,只不過這個文件只會在 ClassPath 下的 jar 包中。

來簡單看下 spring.factories 文件的內(nèi)容吧:

執(zhí)行 loadFactoryNames(EnableAutoConfiguration.class,classLoader)后,得到對應(yīng)的一組 @Configuration 類。

我們就可以通過反射實例化這些類然后注入到 IOC 容器中,最后容器里就有了一系列標(biāo)注了 @Configuration 的 JavaConfig 形式的配置類。

這就是 SpringFactoriesLoader,它本質(zhì)上屬于 Spring 框架私有的一種擴(kuò)展方案,類似于 SPI,Spring Boot 在 Spring 基礎(chǔ)上的很多核心功能都是基于此,希望大家可以理解。

?

另一件武器:Spring 容器的事件監(jiān)聽機(jī)制

過去,事件監(jiān)聽機(jī)制多用于圖形界面編程,比如:點擊按鈕、在文本框輸入內(nèi)容等操作被稱為事件。

而當(dāng)事件觸發(fā)時,應(yīng)用程序作出一定的響應(yīng)則表示應(yīng)用監(jiān)聽了這個事件,而在服務(wù)器端,事件的監(jiān)聽機(jī)制更多的用于異步通知以及監(jiān)控和異常處理。

Java 提供了實現(xiàn)事件監(jiān)聽機(jī)制的兩個基礎(chǔ)類:自定義事件類型擴(kuò)展自 java.util.EventObject、事件的監(jiān)聽器擴(kuò)展自 java.util.EventListener。

來看一個簡單的實例:簡單的監(jiān)控一個方法的耗時。

首先定義事件類型,通常的做法是擴(kuò)展 EventObject,隨著事件的發(fā)生,相應(yīng)的狀態(tài)通常都封裝在此類中:

事件發(fā)布之后,相應(yīng)的監(jiān)聽器即可對該類型的事件進(jìn)行處理,我們可以在方法開始執(zhí)行之前發(fā)布一個 begin 事件。

在方法執(zhí)行結(jié)束之后發(fā)布一個 end 事件,相應(yīng)地,事件監(jiān)聽器需要提供方法對這兩種情況下接收到的事件進(jìn)行處理:

事件監(jiān)聽器接口針對不同的事件發(fā)布實際提供相應(yīng)的處理方法定義,最重要的是,其方法只接收 MethodMonitorEvent 參數(shù),說明這個監(jiān)聽器類只負(fù)責(zé)監(jiān)聽器對應(yīng)的事件并進(jìn)行處理。

有了事件和監(jiān)聽器,剩下的就是發(fā)布事件,然后讓相應(yīng)的監(jiān)聽器監(jiān)聽并處理。

通常情況,我們會有一個事件發(fā)布者,它本身作為事件源,在合適的時機(jī),將相應(yīng)的事件發(fā)布給對應(yīng)的事件監(jiān)聽器:

對于事件發(fā)布者(事件源)通常需要關(guān)注兩點:

  • 在合適的時機(jī)發(fā)布事件。此例中的 methodMonitor() 方法是事件發(fā)布的源頭,其在方法執(zhí)行之前和結(jié)束之后兩個時間點發(fā)布 MethodMonitorEvent 事件,每個時間點發(fā)布的事件都會傳給相應(yīng)的監(jiān)聽器進(jìn)行處理。

?

在具體實現(xiàn)時需要注意的是,事件發(fā)布是順序執(zhí)行,為了不影響處理性能,事件監(jiān)聽器的處理邏輯應(yīng)盡量簡單。

  • 事件監(jiān)聽器的管理。publisher 類中提供了事件監(jiān)聽器的注冊與移除方法,這樣客戶端可以根據(jù)實際情況決定是否需要注冊新的監(jiān)聽器或者移除某個監(jiān)聽器。

?

如果這里沒有提供 remove 方法,那么注冊的監(jiān)聽器示例將一直被 MethodMonitorEventPublisher 引用,即使已經(jīng)廢棄不用了,也依然在發(fā)布者的監(jiān)聽器列表中,這會導(dǎo)致隱性的內(nèi)存泄漏。

?

Spring 容器內(nèi)的事件監(jiān)聽機(jī)制

?

Spring 的 ApplicationContext 容器內(nèi)部中的所有事件類型均繼承自 org.springframework.context.AppliationEvent。

容器中的所有監(jiān)聽器都實現(xiàn) org.springframework.context.ApplicationListener 接口,并且以 bean 的形式注冊在容器中。

一旦在容器內(nèi)發(fā)布 ApplicationEvent 及其子類型的事件,注冊到容器的 ApplicationListener 就會對這些事件進(jìn)行處理。

你應(yīng)該已經(jīng)猜到是怎么回事了。

ApplicationEvent 繼承自 EventObject,Spring 提供了一些默認(rèn)的實現(xiàn),比如:ContextClosedEvent 表示容器在即將關(guān)閉時發(fā)布的事件類型, ContextRefreshedEvent 表示容器在初始化或者刷新的時候發(fā)布的事件類型......

容器內(nèi)部使用 ApplicationListener 作為事件監(jiān)聽器接口定義,它繼承自 EventListener。

ApplicationContext 容器在啟動時,會自動識別并加載 EventListener 類型的bean,一旦容器內(nèi)有事件發(fā)布,將通知這些注冊到容器的 EventListener。

ApplicationContext 接口繼承了 ApplicationEventPublisher 接口,該接口提供了 voidpublishEvent(ApplicationEventevent) 方法定義,不難看出,ApplicationContext 容器擔(dān)當(dāng)?shù)木褪鞘录l(fā)布者的角色。

如果有興趣可以查看 AbstractApplicationContext.publishEvent(ApplicationEventevent) 方法的源碼:ApplicationContext 將事件的發(fā)布以及監(jiān)聽器的管理工作委托給 ApplicationEventMulticaster 接口的實現(xiàn)類。

在容器啟動時,會檢查容器內(nèi)是否存在名為 applicationEventMulticaster 的 ApplicationEventMulticaster 對象實例。

如果有就使用其提供的實現(xiàn),沒有就默認(rèn)初始化一個 SimpleApplicationEventMulticaster 作為實現(xiàn)。

最后,如果我們業(yè)務(wù)需要在容器內(nèi)部發(fā)布事件,只需要為其注入 ApplicationEventPublisher 依賴即可實現(xiàn) ApplicationEventPublisherAware 接口或者 ApplicationContextAware 接口(Aware 接口相關(guān)內(nèi)容請回顧上文)。

?

出神入化:揭秘自動配置原理

典型的 Spring Boot 應(yīng)用的啟動類一般均位于 src/main/java 根路徑下,比如 MoonApplication 類:

其中 @SpringBootApplication 開啟組件掃描和自動配置,而 SpringApplication.run 則負(fù)責(zé)啟動引導(dǎo)應(yīng)用程序。

@SpringBootApplication 是一個復(fù)合 Annotation,它將三個有用的注解組合在一起:

@SpringBootConfiguration 就是 @Configuration,它是 Spring 框架的注解,標(biāo)明該類是一個 JavaConfig 配置類。

而 @ComponentScan 啟用組件掃描,前文已經(jīng)詳細(xì)講解過,這里著重關(guān)注 @EnableAutoConfiguration。

@EnableAutoConfiguration 注解表示開啟 Spring Boot 自動配置功能,Spring Boot 會根據(jù)應(yīng)用的依賴、自定義的 bean、Classpath 下有沒有某個類等等因素來猜測你需要的 bean,然后注冊到 IOC 容器中。

那 @EnableAutoConfiguration 是如何推算出你的需求?首先看下它的定義:

你的關(guān)注點應(yīng)該在 @Import(EnableAutoConfigurationImportSelector.class)上了。

前文說過,@Import 注解用于導(dǎo)入類,并將這個類作為一個 bean 的定義注冊到容器中,這里它將把 EnableAutoConfigurationImportSelector 作為 bean 注入到容器中。

而這個類會將所有符合條件的 @Configuration 配置都加載到容器中,看看它的代碼:

這個類會掃描所有的 Jar 包,將所有符合條件的 @Configuration 配置類注入到容器中,何為符合條件,看看 META-INF/spring.factories 的文件內(nèi)容:

以 DataSourceAutoConfiguration 為例,看看 Spring Boot 是如何自動配置的:

分別說一說:

  • @ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class}):當(dāng) Classpath 中存在 DataSource 或者 EmbeddedDatabaseType 類時才啟用這個配置,否則這個配置將被忽略。

  • @EnableConfigurationProperties(DataSourceProperties.class):將 DataSource 的默認(rèn)配置類注入到 IOC 容器中,DataSourceproperties 定義為:

  • @Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class }):導(dǎo)入其他額外的配置,就以 DataSourcePoolMetadataProvidersConfiguration 為例吧:

DataSourcePoolMetadataProvidersConfiguration 是數(shù)據(jù)庫連接池提供者的一個配置類。

即 Classpath 中存在 org.apache.tomcat.jdbc.pool.DataSource.class,則使用 tomcat-jdbc 連接池,如果 Classpath 中存在 HikariDataSource.class 則使用 Hikari 連接池。

這里僅描述了 DataSourceAutoConfiguration 的冰山一角,但足以說明 Spring Boot 如何利用條件化配置來實現(xiàn)自動配置的。

回顧一下,@EnableAutoConfiguration 中導(dǎo)入了 EnableAutoConfigurationImportSelector 類。

而這個類的 selectImports() 通過 SpringFactoriesLoader 得到了大量的配置類,而每一個配置類則根據(jù)條件化配置來做出決策,以實現(xiàn)自動配置。

整個流程很清晰,但漏了一個大問題: EnableAutoConfigurationImportSelector.selectImports() 是何時執(zhí)行的?

其實這個方法會在容器啟動過程中執(zhí)行: AbstractApplicationContext.refresh(),更多的細(xì)節(jié)在下一小節(jié)中說明。

?

啟動引導(dǎo):Spring Boot 應(yīng)用啟動的秘密

?

SpringApplication 初始化

Spring Boot 整個啟動流程分為兩個步驟:初始化一個 SpringApplication 對象、執(zhí)行該對象的 run 方法。

看下 SpringApplication 的初始化流程,SpringApplication 的構(gòu)造方法中調(diào)用 initialize(Object[] sources) 方法,其代碼如下:

初始化流程中最重要的就是通過 SpringFactoriesLoader 找到 spring.factories 文件中配置的 ApplicationContextInitializer 和 ApplicationListener 兩個接口的實現(xiàn)類名稱,以便后期構(gòu)造相應(yīng)的實例。

ApplicationContextInitializer 的主要目的是在 ConfigurableApplicationContext 做 refresh 之前,對 ConfigurableApplicationContext 實例做進(jìn)一步的設(shè)置或處理。

ConfigurableApplicationContext 繼承自 ApplicationContext,其主要提供了對 ApplicationContext 進(jìn)行設(shè)置的能力。

實現(xiàn)一個 ApplicationContextInitializer 非常簡單,因為它只有一個方法,但大多數(shù)情況下我們沒有必要自定義一個 ApplicationContextInitializer。

即便是 Spring Boot 框架,它默認(rèn)也只是注冊了兩個實現(xiàn),畢竟 Spring 的容器已經(jīng)非常成熟和穩(wěn)定,你沒有必要來改變它。

而 ApplicationListener 的目的就沒什么好說的了,它是 Spring 框架對 Java 事件監(jiān)聽機(jī)制的一種框架實現(xiàn),具體內(nèi)容在前文 Spring 事件監(jiān)聽機(jī)制這個小節(jié)有詳細(xì)講解。

這里主要說說,如果你想為 Spring Boot 應(yīng)用添加監(jiān)聽器,該如何實現(xiàn)?

Spring Boot 提供兩種方式來添加自定義監(jiān)聽器:

  • 通過 SpringApplication.addListeners(ApplicationListener...listeners)或者 SpringApplication.setListeners(Collection>listeners)兩個方法來添加一個或者多個自定義監(jiān)聽器。

  • 既然 SpringApplication 的初始化流程中已經(jīng)從 spring.factories 中獲取到 ApplicationListener 的實現(xiàn)類,那么我們直接在自己的 jar 包的 META-INF/spring.factories 文件中新增配置即可:

關(guān)于 SpringApplication 的初始化,我們就說這么多。

Spring Boot 啟動流程

Spring Boot 應(yīng)用的整個啟動流程都封裝在 SpringApplication.run 方法中,其整個流程真的是太長太長了,但本質(zhì)上就是在 Spring 容器啟動的基礎(chǔ)上做了大量的擴(kuò)展,按照這個思路來看看源碼:

①通過 SpringFactoriesLoader 查找并加載所有的 SpringApplicationRunListeners。

通過調(diào)用 starting() 方法通知所有的 SpringApplicationRunListeners:應(yīng)用開始啟動了。

SpringApplicationRunListeners 其本質(zhì)上就是一個事件發(fā)布者,它在 SpringBoot 應(yīng)用啟動的不同時間點發(fā)布不同應(yīng)用事件類型(ApplicationEvent)。

如果有哪些事件監(jiān)聽者(ApplicationListener)對這些事件感興趣,則可以接收并且處理。

還記得初始化流程中,SpringApplication 加載了一系列 ApplicationListener 嗎?

這個啟動流程中沒有發(fā)現(xiàn)有發(fā)布事件的代碼,其實都已經(jīng)在 SpringApplicationRunListeners 這兒實現(xiàn)了。

簡單的分析一下其實現(xiàn)流程,首先看下 SpringApplicationRunListener 的源碼:

SpringApplicationRunListener 只有一個實現(xiàn)類: EventPublishingRunListener。

①處的代碼只會獲取到一個 EventPublishingRunListener 的實例,我們來看看 starting() 方法的內(nèi)容:

順著這個邏輯,你可以在 ② 處的 prepareEnvironment() 方法的源碼中找到 listeners.environmentPrepared(environment)。

即 SpringApplicationRunListener 接口的第二個方法,那不出你所料, environmentPrepared()又發(fā)布了另外一個事件 ApplicationEnvironmentPreparedEvent。接下來會發(fā)生什么,就不用我多說了吧。

②創(chuàng)建并配置當(dāng)前應(yīng)用將要使用的 Environment。

Environment 用于描述應(yīng)用程序當(dāng)前的運(yùn)行環(huán)境,其抽象了兩個方面的內(nèi)容:配置文件(profile)和屬性(properties)。

開發(fā)經(jīng)驗豐富的同學(xué)對這兩個東西一定不會陌生:不同的環(huán)境(eg:生產(chǎn)環(huán)境、預(yù)發(fā)布環(huán)境)可以使用不同的配置文件,而屬性則可以從配置文件、環(huán)境變量、命令行參數(shù)等來源獲取。

因此,當(dāng) Environment 準(zhǔn)備好后,在整個應(yīng)用的任何時候,都可以從 Environment 中獲取資源。

總結(jié)起來,②處的兩句代碼,主要完成以下幾件事:

  • 判斷 Environment 是否存在,不存在就創(chuàng)建(如果是 Web 項目就創(chuàng)建 StandardServletEnvironment,否則創(chuàng)建 StandardEnvironment)。

  • 配置 Environment:配置 profile 以及 properties。

  • 調(diào)用 SpringApplicationRunListener 的 environmentPrepared() 方法,通知事件監(jiān)聽者:應(yīng)用的 Environment 已經(jīng)準(zhǔn)備好。

③Spring Boot 應(yīng)用在啟動時會輸出這樣的東西:

如果想把這個東西改成自己的涂鴉,你可以研究一下 Banner 的實現(xiàn),這個任務(wù)就留給你們吧。

④根據(jù)是否是 Web 項目,來創(chuàng)建不同的 ApplicationContext 容器。

⑤創(chuàng)建一系列 FailureAnalyzer。

創(chuàng)建流程依然是通過 SpringFactoriesLoader 獲取到所有實現(xiàn) FailureAnalyzer 接口的 class,然后再創(chuàng)建對應(yīng)的實例。FailureAnalyzer 用于分析故障并提供相關(guān)診斷信息。

⑥初始化 ApplicationContext。

主要完成以下工作:

  • 將準(zhǔn)備好的 Environment 設(shè)置給 ApplicationContext。

  • 遍歷調(diào)用所有的 ApplicationContextInitializer 的 initialize() 方法來對已經(jīng)創(chuàng)建好的 ApplicationContext 進(jìn)行進(jìn)一步的處理。

  • 調(diào)用 SpringApplicationRunListener 的 contextPrepared() 方法,通知所有的監(jiān)聽者:ApplicationContext 已經(jīng)準(zhǔn)備完畢。

  • 將所有的 bean 加載到容器中。

  • 調(diào)用 SpringApplicationRunListener 的 contextLoaded() 方法,通知所有的監(jiān)聽者:ApplicationContext 已經(jīng)裝載完畢。

⑦調(diào)用 ApplicationContext 的 refresh() 方法,完成 IOC 容器可用的最后一道工序。

從名字上理解為刷新容器,那何為刷新?就是插手容器的啟動,聯(lián)系一下第一小節(jié)的內(nèi)容。那如何刷新呢?

且看下面代碼:

看看這個方法的實現(xiàn):

獲取到所有的 BeanFactoryPostProcessor 來對容器做一些額外的操作。BeanFactoryPostProcessor 允許我們在容器實例化相應(yīng)對象之前,對注冊到容器的 BeanDefinition 所保存的信息做一些額外的操作。

這里的 getBeanFactoryPostProcessors() 方法可以獲取到 3 個 Processor:

不是有那么多 BeanFactoryPostProcessor 的實現(xiàn)類,為什么這兒只有這 3 個?

因為在初始化流程獲取到的各種 ApplicationContextInitializer 和 ApplicationListener 中,只有上文 3 個做了類似于如下操作:

然后你就可以進(jìn)入到 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors() 方法了。

這個方法除了會遍歷上面的 3 個 BeanFactoryPostProcessor 處理外,還會獲取類型為 BeanDefinitionRegistryPostProcessor 的 bean: org.springframework.context.annotation.internalConfigurationAnnotationProcessor,對應(yīng)的 Class 為 ConfigurationClassPostProcessor。

ConfigurationClassPostProcessor 用于解析處理各種注解,包括:

  • @Configuration

  • @ComponentScan

  • @Import

  • @PropertySource

  • @ImportResource

  • @Bean

當(dāng)處理 @import 注解的時候,就會調(diào)用<自動配置>這一小節(jié)中的 EnableAutoConfigurationImportSelector.selectImports() 來完成自動配置功能。其他的這里不再多講,如果你有興趣,可以查閱參考資料 6。

⑧查找當(dāng)前 context 中是否注冊有 CommandLineRunner 和 ApplicationRunner,如果有則遍歷執(zhí)行它們。

⑨執(zhí)行所有 SpringApplicationRunListener 的 finished() 方法。

這就是 Spring Boot 的整個啟動流程,其核心就是在 Spring 容器初始化并啟動的基礎(chǔ)上加入各種擴(kuò)展點。

這些擴(kuò)展點包括:ApplicationContextInitializer、ApplicationListener 以及各種 BeanFactoryPostProcessor 等等。

你對整個流程的細(xì)節(jié)不必太過關(guān)注,甚至沒弄明白也沒有關(guān)系,你只要理解這些擴(kuò)展點是在何時如何工作的,能讓它們?yōu)槟闼眉纯伞?/p>

整個啟動流程確實非常復(fù)雜,可以查詢參考資料中的部分章節(jié)和內(nèi)容,對照著源碼,多看看,我想最終你都能弄清楚的。

言而總之,Spring 才是核心,理解清楚 Spring 容器的啟動流程,那 Spring Boot 啟動流程就不在話下了。

?

?

轉(zhuǎn)載于:https://my.oschina.net/demons99/blog/2246843

總結(jié)

以上是生活随笔為你收集整理的Spring Boot知识清单的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

久久久午夜电影 | 亚洲视频axxx | 亚洲一级久久 | 精品在线视频一区 | 毛片黄色一级 | 色综合久久久久久久 | 天天射天天干天天爽 | 久草视频免费在线观看 | 正在播放五月婷婷狠狠干 | 久久精品成人热国产成 | 伊人婷婷网| 欧美日韩视频网站 | 欧美韩日精品 | 中文国产在线观看 | 国产激情久久久 | 日韩免费在线观看 | www国产精品com| 丁香五香天综合情 | 手机在线黄色网址 | 天天婷婷| 亚洲精品国产精品乱码在线观看 | 五月天狠狠操 | 超碰成人免费电影 | 天天天操天天天干 | 日本公妇色中文字幕 | 久久久一本精品99久久精品66 | 欧美成人区| 成人av电影免费在线播放 | 丝袜av一区 | 色99之美女主播在线视频 | 综合网伊人 | 欧美analxxxx| 国产精品一区二区三区99 | 国内精品久久久久久久影视简单 | 日韩精品一区二区三区免费观看 | 日韩一级精品 | 亚洲国内精品 | 中文字幕 国产 一区 | 伊人精品在线 | 国产在线观看av | 91久久国产自产拍夜夜嗨 | 欧美一级性视频 | 免费又黄又爽视频 | 91视频高清完整版 | 天天操天天摸天天干 | 亚洲国产日韩在线 | 日韩理论片 | 久久男人视频 | 国产 日韩 在线 亚洲 字幕 中文 | www狠狠操 | 国产精品小视频网站 | www.色国产 | 特级aaa毛片| av黄色亚洲 | 玖玖在线观看视频 | 亚在线播放中文视频 | 在线免费观看视频a | 欧美日韩一区二区三区免费视频 | 青春草国产视频 | 中文理论片 | 免费精品视频在线 | 日日夜夜中文字幕 | 黄色av成人在线观看 | 青春草视频在线播放 | 久久综合丁香 | 免费观看全黄做爰大片国产 | 欧美激情在线网站 | 中文字幕在线观看资源 | 免费成人av电影 | 日日夜夜网 | 日韩精品黄 | 日本中文字幕在线免费观看 | 亚洲区二区 | 婷婷网站天天婷婷网站 | 欧美不卡视频在线 | 四虎影视国产精品免费久久 | 91av色| 日韩在线视 | 日韩理论电影在线 | 激情五月播播久久久精品 | 国产手机视频精品 | 精品国产精品一区二区夜夜嗨 | 中文字幕视频在线播放 | 超碰97在线资源 | 欧美一二三区在线播放 | 中文字幕亚洲五码 | 99精彩视频在线观看免费 | 久久久久激情电影 | 久久国产精品久久国产精品 | 日韩精品一区二区三区第95 | 超碰在线观看97 | 国产高清免费 | 国产精品久久久久影视 | 国产在线精 | 午夜av在线电影 | 中文字幕刺激在线 | 91久久偷偷做嫩草影院 | 久久久男人的天堂 | 欧美aa在线 | 免费观看第二部31集 | 久久婷婷一区二区三区 | 成人精品一区二区三区中文字幕 | 一级做a爱片性色毛片www | 国产一区二区在线精品 | 黄色av一区二区 | 欧美日韩精品影院 | 伊人电影天堂 | 黄色av一区 | 久久精品香蕉视频 | 成人一区二区三区在线观看 | 四虎影视欧美 | 亚洲精品国产拍在线 | 国产精品手机播放 | 精品 一区 在线 | 国产亚洲一级高清 | 人人草在线视频 | 国产一区二区三精品久久久无广告 | 91成人在线视频观看 | 婷婷中文字幕综合 | 久久激情网站 | www.五月天婷婷 | 草久视频在线观看 | 999视频网 | 黄色av免费电影 | 日韩在线观看一区 | 欧美色图一区 | 国产又粗又长又硬免费视频 | 日韩高清精品一区二区 | 色九九影院 | 深夜免费福利 | 99久久精品国产一区 | 操操操天天操 | 婷婷丁香五 | 97精品国产97久久久久久久久久久久 | 一区二区三区精品在线视频 | 天天操天天射天天爽 | 中文字幕免费观看全部电影 | 亚洲精品欧美专区 | 亚洲 av网站| 国产又黄又猛又粗 | 欧美午夜a| 97av在线视频免费播放 | 狠狠色综合网站久久久久久久 | 狠狠操综合网 | 中文字幕在线播出 | 色噜噜噜噜 | 欧美性受极品xxxx喷水 | 激情婷婷丁香 | 国产成人三级在线播放 | 国产品久精国精产拍 | 中文字幕文字幕一区二区 | 日本在线观看视频一区 | 婷婷色中文网 | 97日日碰人人模人人澡分享吧 | 在线视频观看你懂的 | 免费精品久久久 | 欧美日本一区 | 国产高清小视频 | 色综合久久中文字幕综合网 | 91亚洲国产成人久久精品网站 | 久久久五月婷婷 | 国产一区二区网址 | 婷婷在线看 | 久久久久久久久久久久久9999 | 国产在线一区二区三区播放 | 国产在线黄 | 狠狠操狠狠插 | 久久久 精品 | 欧美日韩亚洲精品在线 | 久久国产精品影视 | 国产一级性生活 | 在线观看国产成人av片 | 精品久久五月天 | 久久网址 | 亚洲激情在线观看 | 在线观看国产www | 草久在线播放 | 午夜av激情 | 五月开心婷婷 | 在线观看av片 | 激情五月婷婷综合网 | 在线观看中文字幕网站 | 国产香蕉视频在线播放 | 国产精品乱码久久久久久1区2区 | 久久色视频 | 99免费在线观看视频 | 久久精品国产精品亚洲 | 欧美调教网站 | 免费成视频 | 久久综合久久久 | 麻豆国产精品视频 | 中文字幕免费国产精品 | 在线观看完整版免费 | 日本黄色免费在线 | 在线观看日本高清mv视频 | av电影在线观看完整版一区二区 | 激情五月六月婷婷 | 日韩在线短视频 | 九九九在线 | 午夜狠狠干 | 国产精品久久久久久久久大全 | 久久综合狠狠狠色97 | 91精品视频免费在线观看 | 精品国产伦一区二区三区观看方式 | 国产一区免费视频 | 99在线免费观看视频 | 欧美国产精品久久久久久免费 | 国产91精品一区二区麻豆网站 | 久久综合九色综合欧美就去吻 | 国产精品成人自产拍在线观看 | 久久 地址| 91亚洲影院| 欧美日韩一区二区三区在线免费观看 | 国产福利精品一区二区 | 美女黄频在线观看 | 日韩在线第一 | 99久久婷婷国产综合亚洲 | 亚洲成人黄色 | 97免费在线观看 | 91在线视频免费 | 久久久www成人免费精品张筱雨 | 日韩中文字幕免费视频 | 黄色av成人在线观看 | 玖草影院| 日韩欧美在线一区二区 | 国产视| 久久99视频 | 97香蕉超级碰碰久久免费软件 | 欧美久草视频 | 在线观看国产麻豆 | 天堂av观看| 日韩精品字幕 | 日韩专区 在线 | 蜜桃视频在线视频 | 国产人成看黄久久久久久久久 | 国产一级在线看 | 中文字幕在线一区二区三区 | av大全在线| 日韩精品一区二区三区中文字幕 | 天天搞天天 | 色av男人的天堂免费在线 | 狠狠网 | 久久国产99 | 亚洲精品1区2区3区 超碰成人网 | 亚洲女人天堂成人av在线 | 欧美亚洲精品在线观看 | 精品99免费视频 | 99色视频 | 国产不卡免费 | www.av在线.com | 婷婷天天色 | 国产一区二区在线免费播放 | 久久亚洲热 | 国产精品一区二区三区四 | 激情动态 | 日韩欧美电影 | 中文有码在线视频 | aaa黄色毛片 | 日韩系列在线 | 丁香一区二区 | 国产精品永久久久久久久www | 国产在线精品国自产拍影院 | 国产精品黄色av | 一区 二区 精品 | 国产99久久久国产精品免费看 | 成人a免费视频 | 国产成人专区 | 在线国产日韩 | 久久久久免费视频 | 中文字幕一区在线 | 国产精品18久久久久久vr | 麻豆激情电影 | 天天爱天天色 | 亚洲精品乱码久久 | 成人h在线播放 | 久久久影院一区二区三区 | 一区二区精品视频 | 精品在线一区二区 | 亚洲最大激情中文字幕 | 97超碰成人在线 | av福利电影 | 九色激情网 | 亚洲视频久久久 | 日韩色综合 | 91高清免费在线观看 | 欧美一级电影在线观看 | 91精品影视 | 色视频在线看 | 久久国产精品99国产精 | 免费日韩一区二区三区 | 久久精品国产免费观看 | 亚洲天堂网在线播放 | 91成人精品| 欧美在线aaa | 97视频人人 | 久草综合视频 | av字幕在线 | 国语麻豆 | 精品国产亚洲一区二区麻豆 | 这里有精品在线视频 | 国产视频在线免费 | 激情视频在线观看网址 | 99精品久久精品一区二区 | 国产日韩欧美在线 | 成年免费在线视频 | 天天干,天天干 | 色综合久久中文字幕综合网 | 午夜av不卡 | 久草视频在线资源 | 久久av伊人 | 成人网页在线免费观看 | 91免费高清| 日韩特黄av | 亚洲国产精品一区二区尤物区 | 午夜成人免费影院 | 日韩一区在线免费观看 | 毛片网在线播放 | 99超碰在线观看 | 99精品国产在热久久 | 美女视频黄色免费 | 7777精品伊人久久久大香线蕉 | 一区二区三区中文字幕在线 | 久久第四色 | 久久视频这里有精品 | 在线最新av | 久草在线费播放视频 | 国产成人久久av977小说 | 菠萝菠萝在线精品视频 | 久久久在线| 五月婷婷网站 | 99久久999久久久精玫瑰 | 色偷偷97| 国产免费视频一区二区裸体 | 中国一级片免费看 | 久久久久久久久电影 | 亚洲狠狠婷婷综合久久久 | 国产一区私人高清影院 | 日本黄色a级大片 | 最新国产精品亚洲 | 免费久久精品视频 | 一区二区三区 亚洲 | 美女免费视频黄 | 欧美韩日在线 | 国产午夜一区二区 | 国产在线1区 | 国产精品久久网站 | 六月天色婷婷 | 天天色综合久久 | 国产成人在线观看 | 日本中文一级片 | 黄色毛片视频 | 午夜手机电影 | 国内精品久久久久影院一蜜桃 | www.天堂av| 在线a亚洲视频播放在线观看 | 久久人人爽av | 97成人精品视频在线观看 | 国产免费高清 | 国产午夜精品免费一区二区三区视频 | 色欧美成人精品a∨在线观看 | 国产午夜免费视频 | 91视频免费看片 | 黄色网中文字幕 | 亚洲成av人片一区二区梦乃 | 日韩国产精品一区 | 亚洲91视频 | 国产人成看黄久久久久久久久 | 日韩在线视频一区 | 亚av在线 | 欧美一区在线看 | 亚洲成人av片在线观看 | 中文av资源站| 日韩视频免费 | 亚洲h色精品 | 午夜影院一级片 | 欧美久久久久久久久中文字幕 | 亚洲综合黄色 | 在线观看亚洲国产 | 亚洲免费视频在线观看 | 欧美精品少妇xxxxx喷水 | 97在线免费视频 | 又色又爽又黄高潮的免费视频 | 久久最新视频 | 精品国产乱码久久久久 | 久久免费资源 | 五月婷婷六月丁香 | 少妇bbw揉bbb欧美 | 亚洲精品久久激情国产片 | 国产录像在线观看 | 天天爱天天射 | 欧美一级性视频 | 免费看国产黄色 | 欧美最猛性xxxxx(亚洲精品) | 永久免费的av电影 | 一级片视频在线 | 91资源在线 | 在线观看免费av网 | 国产一区视频在线 | 女人18精品一区二区三区 | 日韩手机在线 | 国产一区二区三区久久久 | 久久久不卡影院 | 久久免费公开视频 | 日韩电影在线视频 | 国产午夜三级一区二区三桃花影视 | 手机av电影在线观看 | 成人在线免费小视频 | 国产又粗又猛又色又黄视频 | 精品一区二区视频 | 国产一线二线三线性视频 | 欧美精品久久久久久久久老牛影院 | 国产精品久久久区三区天天噜 | 久久国语 | 国产视频一区在线 | 91九色在线观看视频 | 亚州精品在线视频 | 欧美激情视频一二区 | 日韩资源在线 | 999久久国精品免费观看网站 | 久久久精品网 | 中文字幕在线观看资源 | 国产一区二区免费看 | 三级av中文字幕 | 韩国精品在线 | 五月天com | 色综合天天干 | 91视频麻豆 | 亚洲综合成人婷婷小说 | 成年人在线播放视频 | 国产二区电影 | 美女在线免费观看视频 | 国产成人在线免费观看 | 国产精品久久久久久久久久直播 | 久久九九精品久久 | 亚洲高清精品在线 | 国产又粗又猛又黄又爽的视频 | 在线视频99 | 激情视频91 | 久久香蕉国产 | 国产91勾搭技师精品 | 三级av免费 | 一区二区三区四区影院 | 少妇搡bbbb搡bbb搡忠贞 | 久久桃花网 | 国产精品一区二区免费看 | 精品在线视频一区 | 一区二区三区视频网站 | 最近免费中文字幕mv在线视频3 | 四虎在线观看精品视频 | 亚洲成人国产 | 天天在线操 | 99中文字幕在线观看 | 国产一区二区三区网站 | 在线视频一区二区 | 国产欧美精品一区二区三区 | 久久黄色免费视频 | 国产男女爽爽爽免费视频 | 福利区在线观看 | 麻豆影视在线免费观看 | 正在播放五月婷婷狠狠干 | 西西444www大胆无视频 | 免费观看一级 | 欧美激情精品久久 | 波多在线视频 | 国产精品一区在线播放 | 在线免费观看黄网站 | 97国产在线 | 超碰97国产精品人人cao | 特级xxxxx欧美 | 中文字幕在线有码 | 免费成人看片 | 91精品电影 | 婷婷久久综合网 | 精品久久久久久国产91 | 69久久久久久久 | 欧美精品一区二区三区四区在线 | 91大片网站 | 日韩中字在线观看 | 国产一级精品在线观看 | 久久久99国产精品免费 | 久久电影色 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 在线亚洲人成电影网站色www | 国产视频午夜 | 天天干天天搞天天射 | 国产精品免费视频久久久 | www久久久 | 欧美日韩一二三四区 | 日本中文字幕在线一区 | 99欧美精品 | 91精品播放 | 在线免费成人 | 激情视频免费在线观看 | 天天操夜夜操天天射 | 午夜三级毛片 | 国产成人一区二区三区免费看 | 国产在线不卡视频 | 久99久在线视频 | 人人精品久久 | 一区二区不卡在线观看 | 天天干视频在线 | 国产视频 亚洲视频 | av在线h| 毛片二区| 久久国际影院 | 免费观看一级视频 | 日本精品视频网站 | 成人a v视频 | 一区二区三区不卡在线 | 国产91探花 | 成人国产网址 | www.在线观看av | 欧美福利网站 | 久久久国产一区二区三区四区小说 | 日韩高清精品一区二区 | 99热都是精品 | 亚洲精品tv久久久久久久久久 | 顶级bbw搡bbbb搡bbbb | 不卡的av在线| 激情婷婷六月 | 一区二区三区在线看 | 色婷婷av一区| 免费网站黄 | 久久久亚洲麻豆日韩精品一区三区 | 在线观看韩日电影免费 | 亚洲精品小区久久久久久 | 亚洲精品男人天堂 | 五月天丁香视频 | 9999精品| 天天综合网天天综合色 | 91传媒免费观看 | 国产一级电影在线 | 欧美黄色特级片 | 伊人看片 | 欧美色操 | 综合国产视频 | 中文不卡视频在线 | 久久国产高清视频 | 一区二区三区日韩在线观看 | 中文字幕在线免费看线人 | 狠狠狠色丁香综合久久天下网 | 久久激情网站 | 亚洲免费一级电影 | 亚洲欧美视频一区二区三区 | 99精品一区| 中国一级特黄毛片大片久久 | 福利视频网址 | 五月天中文字幕mv在线 | 9999国产精品 | 亚洲欧美日韩国产精品一区午夜 | 亚洲五月 | 91麻豆精品国产91久久久无限制版 | 四虎小视频 | 免费看一级一片 | 亚洲精品乱码久久久久久高潮 | 国产精品久久久久久影院 | 久久久久久国产精品 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 国产免费久久精品 | 草久久精品 | 91精品在线免费观看视频 | 国产 在线 高清 精品 | 国产一区成人在线 | 国产一区麻豆 | 国产日韩欧美中文 | 美国人与动物xxxx | 精品国产区 | 色偷偷88888欧美精品久久久 | 中文字幕在线观看日本 | www.五月激情.com | 欧美乱码精品一区二区 | 在线观看视频一区二区三区 | 人人看人人 | 国产精品欧美久久久久三级 | 午夜久久久久久久久久影院 | 在线成人高清电影 | 成人在线你懂得 | 日韩最新av在线 | 六月婷婷色 | 精品国产一区二区三区久久影院 | 91视频88av| 91日韩精品 | 中文字幕 欧美性 | 久久精品一区八戒影视 | 久久久久www | 日韩中文在线电影 | 精品国产电影 | 国产精品999久久久 久产久精国产品 | 美女网站视频一区 | 久久精品国产亚洲a | 亚州成人av在线 | 久草视频一区 | 亚洲精品xxx | 国产特级毛片aaaaaaa高清 | 国产小视频免费在线网址 | 91麻豆精品国产91久久久更新时间 | 国产五月天婷婷 | 天天插天天干天天操 | 亚洲不卡在线 | 91综合视频在线观看 | 日韩sese| 天天操天天射天天插 | 人人爱天天操 | 久久中文精品视频 | 精品999国产| 久久不射影院 | 免费看亚洲毛片 | 69国产盗摄一区二区三区五区 | av在线精品 | 99精品视频在线看 | 五月天婷婷丁香花 | www.五月婷| 国产亚洲免费的视频看 | 日日操狠狠干 | 99精品久久精品一区二区 | 在线免费观看黄 | 日韩色在线观看 | www.69xx | 久久夜色精品亚洲噜噜国4 午夜视频在线观看欧美 | 日日夜夜噜噜噜 | 久久er99热精品一区二区三区 | 麻豆久久精品 | 日本久久免费电影 | 五月婷香蕉久色在线看 | 国内精品视频一区二区三区八戒 | 日韩免费三级 | 日本免费一二三区 | 国产色在线观看 | 日韩欧美综合 | 在线播放第一页 | av在线播放免费 | 欧美在线视频一区二区 | 久久久国产精品亚洲一区 | av超碰免费在线 | 国产精品不卡视频 | 一区三区视频在线观看 | 久久久久久久久网站 | 免费男女羞羞的视频网站中文字幕 | 在线激情网 | 国产一区二区电影在线观看 | 午夜精品久久久久久中宇69 | 黄色av一区二区三区 | 黄色中文字幕 | 久久激情视频 久久 | 久久理论电影网 | 色综合久久88色综合天天 | 亚洲美女在线国产 | 久久影院中文字幕 | 久久久免费国产 | 天天看天天干 | 不卡的av在线| 国产成人精品一二三区 | 久久毛片高清国产 | 成人h电影在线观看 | 黄免费在线观看 | 成人亚洲精品久久久久 | 97超级碰| 久久电影网站中文字幕 | 亚洲精品视频在线观看免费视频 | 久久精品美女视频网站 | 91视频在线观看免费 | 久久伊人综合 | 人人看黄色 | 国产成人一区二区三区免费看 | av永久网址 | 欧美在线观看视频 | 久久综合九色欧美综合狠狠 | 99精品视频在线免费观看 | 久久久一本精品99久久精品 | 国内精品久久久久久 | 高清免费在线视频 | 园产精品久久久久久久7电影 | 日韩欧美精品在线 | 九九热免费观看 | 亚洲国产伊人 | 国产精品麻豆三级一区视频 | 亚av在线| 99视频网站 | 国产黄色理论片 | 国产91精品看黄网站 | 久久精精品 | 99久久精品日本一区二区免费 | 91av视频在线免费观看 | 国产在线观看中文字幕 | 高清av在线 | a黄色 | 久久激情日本aⅴ | 亚洲午夜大片 | 欧美日韩视频在线 | 亚洲成av人片在线观看香蕉 | 精品久久精品 | 美女网站在线播放 | 高清在线观看av | 91精品国产99久久久久久久 | 亚洲美女视频网 | 欧美日韩二三区 | 91爱爱网址 | 黄色美女免费网站 | 91成人免费观看视频 | 天躁狠狠躁 | 中文字幕在线第一页 | 青青河边草免费视频 | 精品国产乱码久久 | 97精品久久 | av免费在线观看1 | 久久久久久精 | 久久综合丁香 | 九九九热精品免费视频观看 | 国产传媒中文字幕 | 中文字幕乱码在线播放 | 四虎影视成人精品 | av电影 一区二区 | 在线观看亚洲电影 | av网站播放 | 国产一区二区在线播放视频 | 激情图片qvod | 久久国产品| 在线成人一区 | 国产精品永久免费视频 | 精品在线视频播放 | av 一区 二区 久久 | 99精品在线看 | 免费在线观看亚洲视频 | 日韩a级黄色 | 一级欧美一级日韩 | 日韩在线观看高清 | 黄色亚洲 | 精品亚洲免a | 欧美 激情 国产 91 在线 | 国产又粗又猛又黄又爽的视频 | 欧美日本日韩aⅴ在线视频 插插插色综合 | 麻豆久久久 | 99r国产精品 | 日韩电影久久久 | 亚洲一级影院 | 国产成人综| 国产精品国产毛片 | 久久综合久久伊人 | 免费色av| 99色婷婷 | 久久久久久久亚洲精品 | 精品一区二区免费 | 国产一级二级在线播放 | 国产成人三级一区二区在线观看一 | 色婷婷成人网 | 97国产在线 | 91麻豆精品国产91久久久无限制版 | 日日婷婷夜日日天干 | 国产精品乱码久久久久久1区2区 | 午夜精品久久久 | 久久综合偷偷噜噜噜色 | 精品中文字幕在线观看 | 91大神电影 | 国产精品久久久久久久电影 | 天天躁日日 | 在线性视频日韩欧美 | 国产黄色理论片 | 91麻豆操 | 日韩精品亚洲专区在线观看 | 在线视频观看你懂的 | 欧美一级性生活视频 | 99国内精品久久久久久久 | 欧美日韩性视频在线 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 色综合婷婷久久 | 久久综合久久八八 | 97超碰在线久草超碰在线观看 | 国产精品大尺度 | 精品国产资源 | 国产精品激情 | 在线免费av电影 | 91探花在线视频 | 色综合人人 | 天堂在线一区二区三区 | 五月激情五月激情 | 国产一级在线观看视频 | 国产精品久久久久高潮 | 91一区二区三区久久久久国产乱 | 国产成人精品一区二区在线观看 | 最近最新mv字幕免费观看 | 亚洲三级在线免费观看 | 日韩专区一区二区 | 在线观看视频在线 | 精品一二区 | 黄色综合 | 国产永久网站 | 国产精品一区二区三区电影 | 国产精品国产自产拍高清av | 一区二区三区高清 | 香蕉久草 | 玖玖玖精品 | 在线观看免费高清视频大全追剧 | 日韩久久在线 | 亚洲国产中文在线观看 | 免费的国产精品 | 91看片成人 | 在线观看色网 | 九九热国产视频 | 日韩大陆欧美高清视频区 | av在线一| 国产不卡视频 | 婷婷久久婷婷 | 日韩字幕在线观看 | 又黄又爽免费视频 | 国产免费高清 | 国产精品乱码一区二三区 | 欧美日韩一区二区免费在线观看 | 国产精品ⅴa有声小说 | 黄色com| 中文字幕一区二区三区四区久久 | 色99导航| 91成年人在线观看 | 欧美日韩性生活 | 日韩欧美视频一区二区三区 | 欧美专区日韩专区 | 99精品视频免费在线观看 | 久久免费福利视频 | 婷色在线| 日韩在线电影观看 | 人人精久 | 日韩理论 | 欧美日韩国产精品一区二区 | 手机在线观看国产精品 | 亚洲综合射 | www.夜色.com| 成人小视频在线播放 | 成 人 黄 色 免费播放 | 久久久免费精品国产一区二区 | 在线免费91 | 午夜精品久久久久久久久久 | 欧美精品久久久久久 | 99久久精品午夜一区二区小说 | 免费视频久久久久久久 | 在线看一区二区 | 久久成人高清视频 | 91在线免费看片 | 免费看三级黄色片 | 日韩三级av | 久久成人精品 | 99久久精品日本一区二区免费 | 婷婷深爱 | 日本久久久亚洲精品 | 日韩免费一区 | 男女视频91 | 天天干天天干天天干天天干天天干天天干 | 九九久久国产精品 | 91免费试看| 国产成人综合图片 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 久久视频精品在线 | www.久久免费视频 | 性色av一区二区 | 成年人免费在线观看 | 亚洲精品美女在线观看 | 亚洲国产黄色片 | 中文字幕国产在线 | 成人av片在线观看 | 天天做天天爱天天综合网 | 精品国产人成亚洲区 | 97电院网手机版 | 黄色国产精品 | 九九热只有这里有精品 | 成人久久视频 | 中文字幕精品www乱入免费视频 | 夜夜躁狠狠躁日日躁 | 园产精品久久久久久久7电影 | 精品国产一区二区三区久久影院 | 91亚洲精品国偷拍自产在线观看 | 婷婷丁香激情 | 日三级在线| av线上看 | 日韩有码在线观看视频 | 91精品一区国产高清在线gif | 欧美日韩在线观看不卡 | 国产真实精品久久二三区 | 在线观看播放av | 亚洲精品视频第一页 | 久久久免费毛片 | 一个色综合网站 | 国产 在线 高清 精品 | 91精品久久久久久综合乱菊 | 国内精品国产三级国产aⅴ久 | 国产午夜精品在线 | 国产精品69久久久久 | 97精品超碰一区二区三区 | 丁香婷婷激情国产高清秒播 | 成人在线免费看视频 | 在线观看精品一区 | 日日夜夜免费精品视频 | 久久福利国产 | 国产一区在线不卡 | 一本到视频在线观看 | 91一区啪爱嗯打偷拍欧美 | 久久久久亚洲精品中文字幕 | 特级西西www44高清大胆图片 | av不卡中文字幕 | 色诱亚洲精品久久久久久 | a级片网站 | 五月天欧美精品 | 在线免费观看欧美日韩 | 欧美少妇bbwhd | 婷婷五月在线视频 | 欧美精品国产综合久久 | 久久国产91 | 天天爽夜夜爽人人爽一区二区 | 成人午夜精品福利免费 | 精品欧美乱码久久久久久 | 国产中文字幕亚洲 | 亚州国产精品久久久 | 亚洲人成影院在线 | 国产1区2区 | 午夜精品麻豆 | 国产999在线观看 | 亚洲精品一区二区久 | 激情婷婷av | av东方在线 | 视频成人免费 | 一区二区毛片 | 一级黄色av | 国模一区二区三区四区 | 日韩成人高清在线 | 久久电影中文字幕视频 | 亚洲乱码国产乱码精品天美传媒 | 五月婷婷另类国产 | 一区二区三区韩国免费中文网站 | 精品不卡av | 91中文字幕一区 | 国产欧美精品一区二区三区 | 精品网站999www | 久久久久久久久久久久久影院 | 国产日韩精品一区二区在线观看播放 | 五月天色网站 | 国产特黄色片 | 久久99精品波多结衣一区 | 在线观看亚洲视频 | 天天色天天干天天 | a久久久久久| 日韩精品免费一区二区在线观看 | 深夜福利视频一区二区 | 在线视频a | 日韩精品免费在线 | 97超碰资源总站 | 天堂在线成人 | 99精品一区二区三区 | 97国产超碰| 久久久久女人精品毛片九一 | 综合国产在线观看 | 日日摸日日爽 | 亚洲区另类春色综合小说校园片 | 九九热视频在线播放 | 日韩电影精品 | 国产手机视频精品 | 国产精品久久久久毛片大屁完整版 | 国产 一区二区三区 在线 | 中文字幕在线观看网站 | 国产一区自拍视频 | 中文字幕av全部资源www中文字幕在线观看 | 国产午夜麻豆影院在线观看 | 亚洲一区精品二人人爽久久 | 99精品国产免费久久久久久下载 | 久久黄色免费 | 国语精品免费视频 | 久草在线中文888 | 久精品视频在线观看 | av免费播放 | 91探花在线| 99在线视频播放 | 高潮久久久久久久久 | 国产亚洲午夜高清国产拍精品 | av在线精品 | 日韩精品免费在线视频 | 国产美腿白丝袜足在线av | 一级免费黄色 | 在线免费视频 你懂得 | 天天操天天干天天插 | 看片一区二区三区 | 国产精品亚洲精品 | 成 人 黄 色视频免费播放 | 少妇bbw揉bbb欧美 | 日韩免费电影网站 | 成人av免费网站 | 精品久久一二三区 | 国产精品18久久久久vr手机版特色 | 在线观看一区视频 | 韩国av一区 | 午夜视频免费 | 鲁一鲁影院 | 一级c片| 精品国偷自产国产一区 | 91国内产香蕉 | 成人黄色片免费看 | 中文字幕久久久精品 | 综合久久综合久久 | 777久久久 | 亚洲男人天堂a | 中文免费 | 日韩影视大全 | 在线电影日韩 |