<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## Spring概述 ### 什么是spring? * Spring是**一個輕量級Java開發框架**,最早有**Rod Johnson**創建,目的是為了解決企業級應用開發的業務邏輯層和其他各層的耦合問題。它是一個分層的JavaSE/JavaEE full-stack(一站式)輕量級開源框架,為開發Java應用程序提供全面的基礎架構支持。Spring負責基礎架構,因此Java開發者可以專注于應用程序的開發。 * Spring最根本的使命是**解決企業級應用開發的復雜性,即簡化Java開發**。 * Spring可以做很多事情,它為企業級開發提供給了豐富的功能,但是這些功能的底層都依賴于它的兩個核心特性,也就是**依賴注入(dependency injection,DI)**和**面向切面編程(aspect-oriented programming,AOP)**。 **為了降低Java開發的復雜性,Spring采取了以下4種關鍵策略** * 基于POJO的輕量級和最小侵入性編程; * 通過依賴注入和面向接口實現松耦合; * 基于切面和慣例進行聲明式編程; * 通過切面和模板減少樣板式代碼。 ### Spring的倆大核心概念 * IOC(控制翻轉): * 控制翻轉,也叫依賴注入,他就是不會直接創建對象,只是把對象聲明出來,在代碼 中不直接與對象和服務進行連接,但是在配置文件中描述了哪一項組件需要哪一項服 務,容器將他們組件起來。在一般的IOC場景中容器創建了所有的對象,并設置了必 要的屬性將他們聯系在一起,等到需要使用的時候才把他們聲明出來,使用注解就跟 方便了,容器會自動根據注解把對象組合起來 * AOP(面對切面編程) * 面對切面編程,這是一種編程模式,他允許程序員通過自定義的橫切點進行模塊 化,將那些影響多個類的行為封裝到課重用的模塊中。 例子:比如日志輸出,不使用AOP的話就需要把日志的輸出語句放在所有類中,方法 中,但是有了AOP就可以把日志輸出語句封裝一個可重用模塊,在以聲明的方式將他 們放在類中,每次使用類就自動完成了日志輸出。 ### Spring框架的設計目標,設計理念,和核心是什么 * **Spring設計目標**:Spring為開發者提供一個一站式輕量級應用開發平臺; * **Spring設計理念**:在JavaEE開發中,支持POJO和JavaBean開發方式,使應用面向接口開發,充分支持OOP(面向對象)設計方法;Spring通過IOC容器實現對象耦合關系的管理,并實現依賴反轉,將對象之間的依賴關系交給IOC容器,實現解耦; * **Spring框架的核心**:IOC容器和AOP模塊。通過IOC容器管理POJO對象以及他們之間的耦合關系;通過AOP以動態非侵入的方式增強服務。 * IOC讓相互協作的組件保持松散的耦合,而AOP編程允許你把遍布于應用各層的功能分離出來形成可重用的功能組件。 ### Spring的優缺點是什么? **優點** * 方便解耦,簡化開發 Spring就是一個大工廠,可以將所有對象的創建和依賴關系的維護,交給Spring管理。 * AOP編程的支持 Spring提供面向切面編程,可以方便的實現對程序進行權限攔截、運行監控等功能。 * 聲明式事務的支持 只需要通過配置就可以完成對事務的管理,而無需手動編程。 * 方便程序的測試 Spring對Junit4支持,可以通過注解方便的測試Spring程序。 * 方便集成各種優秀框架 Spring不排斥各種優秀的開源框架,其內部提供了對各種優秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。 * 降低JavaEE API的使用難度 Spring對JavaEE開發中非常難用的一些API(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些API應用難度大大降低。 **缺點** * Spring明明一個很輕量級的框架,卻給人感覺大而全 * Spring依賴反射,反射影響性能 * 使用門檻升高,入門Spring需要較長時間 ### Spring有哪些應用場景 * **應用場景**:JavaEE企業應用開發,包括SSH、SSM等 **Spring價值**: * Spring是非侵入式的框架,目標是使應用程序代碼對框架依賴最小化; * Spring提供一個一致的編程模型,使應用直接使用POJO開發,與運行環境隔離開來; * Spring推動應用設計風格向面向對象和面向接口開發轉變,提高了代碼的重用性和可測試性; ### Spring由哪些模塊組成? * Spring 總共大約有 20 個模塊, 由 1300 多個不同的文件構成。 而這些組件被分別整合在`核心容器(Core Container)` 、 `AOP(Aspect Oriented Programming)和設備支持(Instrmentation)` 、`數據訪問與集成(Data Access/Integeration)` 、 `Web`、 `消息(Messaging)` 、 `Test`等 6 個模塊中。 以下是 Spring 5 的模塊結構圖: ![在這里插入圖片描述](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/4/13/1717443eb25a3d9b~tplv-t2oaga2asx-watermark.awebp) * spring core:提供了框架的基本組成部分,包括控制反轉(Inversion of Control,IOC)和依賴注入(Dependency Injection,DI)功能。 * spring beans:提供了BeanFactory,是工廠模式的一個經典實現,Spring將管理對象稱為Bean。 * spring context:構建于 core 封裝包基礎上的 context 封裝包,提供了一種框架式的對象訪問方法。 * spring jdbc:提供了一個JDBC的抽象層,消除了煩瑣的JDBC編碼和數據庫廠商特有的錯誤代碼解析, 用于簡化JDBC。 * spring aop:提供了面向切面的編程實現,讓你可以自定義攔截器、切點等。 * spring Web:提供了針對 Web 開發的集成特性,例如文件上傳,利用 servlet listeners 進行 ioc 容器初始化和針對 Web 的 ApplicationContext。 * spring test:主要為測試提供支持的,支持使用JUnit或TestNG對Spring組件進行單元測試和集成測試。 ### Spring 框架中都用到了哪些設計模式? 1. 工廠模式:BeanFactory就是簡單工廠模式的體現,用來創建對象的實例; 2. 單例模式:Bean默認為單例模式。 3. 代理模式:Spring的AOP功能用到了JDK的動態代理和CGLIB字節碼生成技術; 4. 模板方法:用來解決代碼重復的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。 5. 觀察者模式:定義對象鍵一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都會得到通知被制動更新,如Spring中listener的實現–ApplicationListener。 ### 詳細講解一下核心容器(spring context應用上下文) 模塊 * 這是基本的Spring模塊,提供spring 框架的基礎功能,BeanFactory 是 任何以spring為基礎的應用的核心。Spring 框架建立在此模塊之上,它使Spring成為一個容器。 * Bean 工廠是工廠模式的一個實現,提供了控制反轉功能,用來把應用的配置和依賴從真正的應用代碼中分離。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根據XML文件中的定義加載beans。該容器從XML 文件讀取配置元數據并用它去創建一個完全配置的系統或應用。 ### Spring框架中有哪些不同類型的事件 * Spring 提供了以下5種標準的事件: 1. 上下文更新事件(ContextRefreshedEvent):在調用ConfigurableApplicationContext 接口中的refresh()方法時被觸發。 2. 上下文開始事件(ContextStartedEvent):當容器調用ConfigurableApplicationContext的Start()方法開始/重新開始容器時觸發該事件。 3. 上下文停止事件(ContextStoppedEvent):當容器調用ConfigurableApplicationContext的Stop()方法停止容器時觸發該事件。 4. 上下文關閉事件(ContextClosedEvent):當ApplicationContext被關閉時觸發該事件。容器被關閉時,其管理的所有單例Bean都被銷毀。 5. 請求處理事件(RequestHandledEvent):在Web應用中,當一個http請求(request)結束觸發該事件。如果一個bean實現了ApplicationListener接口,當一個ApplicationEvent 被發布以后,bean會自動被通知。 ### Spring 應用程序有哪些不同組件? **Spring 應用一般有以下組件:** * 接口 - 定義功能。 * Bean 類 - 它包含屬性,setter 和 getter 方法,函數等。 * Bean 配置文件 - 包含類的信息以及如何配置它們。 * Spring 面向切面編程(AOP) - 提供面向切面編程的功能。 * 用戶程序 - 它使用接口。 ### 使用 Spring 有哪些方式? **使用 Spring 有以下方式:** * 作為一個成熟的 Spring Web 應用程序。 * 作為第三方 Web 框架,使用 Spring Frameworks 中間層。 * 作為企業級 Java Bean,它可以包裝現有的 POJO(Plain Old Java Objects)。 * 用于遠程使用。 ## Spring控制反轉(IOC) ### 什么是Spring IOC 容器? * 控制反轉即IOC (Inversion of Control),它把傳統上由程序代碼直接操控的對象的調用權交給容器,通過容器來實現對象組件的裝配和管理。所謂的“控制反轉”概念就是對組件對象控制權的轉移,從程序代碼本身轉移到了外部容器。 * Spring IOC 負責創建對象,管理對象(通過依賴注入(DI),裝配對象,配置對象,并且管理這些對象的整個生命周期。 ### 控制反轉(IOC)有什么作用 * 管理對象的創建和依賴關系的維護。對象的創建并不是一件簡單的事,在對象關系比較復雜時,如果依賴關系需要程序猿來維護的話,那是相當頭疼的 * 解耦,由容器去維護具體的對象 * 托管了類的產生過程,比如我們需要在類的產生過程中做一些處理,最直接的例子就是代理,如果有容器程序可以把這部分處理交給容器,應用程序則無需去關心類是如何完成代理的 ### IOC的優點是什么? * IOC 或 依賴注入把應用的代碼量降到最低。 * 它使應用容易測試,單元測試不再需要單例和JNDI查找機制。 * 最小的代價和最小的侵入性使松散耦合得以實現。 * IOC容器支持加載服務時的餓漢式初始化和懶加載。 ### Spring IOC 的實現機制 * Spring 中的 IOC 的實現原理就是工廠模式加反射機制。 * 示例: ~~~ interface Fruit { public abstract void eat(); } class Apple implements Fruit { public void eat(){ System.out.println("Apple"); } } class Orange implements Fruit { public void eat(){ System.out.println("Orange"); } } class Factory { public static Fruit getInstance(String ClassName) { Fruit f=null; try { f=(Fruit)Class.forName(ClassName).newInstance(); } catch (Exception e) { e.printStackTrace(); } return f; } } class Client { public static void main(String[] a) { Fruit f=Factory.getInstance("io.github.dunwu.spring.Apple"); if(f!=null){ f.eat(); } } } 復制代碼 ~~~ ### Spring 的 IOC支持哪些功能 * Spring 的 IOC 設計支持以下功能: * 其中,最重要的就是依賴注入,從 XML 的配置上說,即 ref 標簽。對應 Spring RuntimeBeanReference 對象。 * 對于 IOC 來說,最重要的就是容器。容器管理著 Bean 的生命周期,控制著 Bean 的依賴注入。 ### BeanFactory 和 ApplicationContext有什么區別? * BeanFactory和ApplicationContext是Spring的兩大核心接口,都可以當做Spring的容器。其中ApplicationContext是BeanFactory的子接口。 * 依賴關系 * BeanFactory:是Spring里面最底層的接口,包含了各種Bean的定義,讀取bean配置文檔,管理bean的加載、實例化,控制bean的生命周期,維護bean之間的依賴關系。 * ApplicationContext接口作為BeanFactory的派生,除了提供BeanFactory所具有的功能外,還提供了更完整的框架功能: * 繼承MessageSource,因此支持國際化。 * 統一的資源文件訪問方式。 * 提供在監聽器中注冊bean的事件。 * 同時加載多個配置文件。 * 載入多個(有繼承關系)上下文 ,使得每一個上下文都專注于一個特定的層次,比如應用的web層。 * 加載方式 * BeanFactroy采用的是延遲加載形式來注入Bean的,即只有在使用到某個Bean時(調用getBean()),才對該Bean進行加載實例化。這樣,我們就不能發現一些存在的Spring的配置問題。如果Bean的某一個屬性沒有注入,BeanFacotry加載后,直至第一次使用調用getBean方法才會拋出異常。 * ApplicationContext,它是在容器啟動時,一次性創建了所有的Bean。這樣,在容器啟動時,我們就可以發現Spring中存在的配置錯誤,這樣有利于檢查所依賴屬性是否注入。 ApplicationContext啟動后預載入所有的單實例Bean,通過預載入單實例bean ,確保當你需要的時候,你就不用等待,因為它們已經創建好了。 `相對于基本的BeanFactory,ApplicationContext 唯一的不足是占用內存空間。當應用程序配置Bean較多時,程序啟動較慢。` * 創建方式 * BeanFactory通常以編程的方式被創建,ApplicationContext還能以聲明的方式創建,如使用ContextLoader。 * 注冊方式 * BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區別是:BeanFactory需要手動注冊,而ApplicationContext則是自動注冊。 ### Spring 如何設計容器的,BeanFactory和ApplicationContext的關系詳解 * Spring 作者 Rod Johnson 設計了兩個接口用以表示容器。 * BeanFactory * ApplicationContext * BeanFactory 簡單粗暴,可以理解為就是個 HashMap,Key 是 BeanName,Value 是 Bean 實例。通常只提供注冊(put),獲取(get)這兩個功能。我們可以稱之為 **“低級容器”**。 * ApplicationContext 可以稱之為 **“高級容器”**。因為他比 BeanFactory 多了更多的功能。他繼承了多個接口。因此具備了更多的功能。例如資源的獲取,支持多種消息(例如 JSP tag 的支持),對 BeanFactory 多了工具級別的支持等待。所以你看他的名字,已經不是 BeanFactory 之類的工廠了,而是 “應用上下文”, 代表著整個大容器的所有功能。該接口定義了一個 refresh 方法,此方法是所有閱讀 Spring 源碼的人的最熟悉的方法,用于刷新整個容器,即重新加載/刷新所有的 bean。 * 當然,除了這兩個大接口,還有其他的輔助接口,這里就不介紹他們了。 * BeanFactory和ApplicationContext的關系 * 為了更直觀的展示 “低級容器” 和 “高級容器” 的關系,這里通過常用的 ClassPathXmlApplicationContext 類來展示整個容器的層級 UML 關系。 ![img](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/4/13/1717443eb634549a~tplv-t2oaga2asx-watermark.awebp) * 有點復雜? 先不要慌,我來解釋一下。 * 最上面的是 BeanFactory,下面的 3 個綠色的,都是功能擴展接口,這里就不展開講。 * 看下面的隸屬 ApplicationContext 粉紅色的 “高級容器”,依賴著 “低級容器”,這里說的是依賴,不是繼承哦。他依賴著 “低級容器” 的 getBean 功能。而高級容器有更多的功能:支持不同的信息源頭,可以訪問文件資源,支持應用事件(Observer 模式)。 * 通常用戶看到的就是 “高級容器”。 但 BeanFactory 也非常夠用啦! * 左邊灰色區域的是 “低級容器”, 只負載加載 Bean,獲取 Bean。容器其他的高級功能是沒有的。例如上圖畫的 refresh 刷新 Bean 工廠所有配置,生命周期事件回調等。 **小結** * 說了這么多,不知道你有沒有理解Spring IOC? 這里小結一下:IOC 在 Spring 里,只需要低級容器就可以實現,2 個步驟: 1. 加載配置文件,解析成 BeanDefinition 放在 Map 里。 2. 調用 getBean 的時候,從 BeanDefinition 所屬的 Map 里,拿出 Class 對象進行實例化,同時,如果有依賴關系,將遞歸調用 getBean 方法 —— 完成依賴注入。 * 上面就是 Spring 低級容器(BeanFactory)的 IOC。 * 至于高級容器 ApplicationContext,他包含了低級容器的功能,當他執行 refresh 模板方法的時候,將刷新整個容器的 Bean。同時其作為高級容器,包含了太多的功能。一句話,他不僅僅是 IOC。他支持不同信息源頭,支持 BeanFactory 工具類,支持層級容器,支持訪問文件資源,支持事件發布通知,支持接口回調等等。 ### ApplicationContext通常的實現是什么? * **FileSystemXmlApplicationContext** :此容器從一個XML文件中加載beans的定義,XML Bean 配置文件的全路徑名必須提供給它的構造函數。 * **ClassPathXmlApplicationContext**:此容器也從一個XML文件中加載beans的定義,這里,你需要正確設置classpath因為這個容器將在classpath里找bean配置。 * **WebXmlApplicationContext**:此容器加載一個XML文件,此文件定義了一個WEB應用的所有bean。 ### 什么是Spring的依賴注入? * 控制反轉IOC是一個很大的概念,可以用不同的方式來實現。其主要實現方式有兩種:依賴注入和依賴查找依賴注入:相對于IOC而言,依賴注入(DI)更加準確地描述了IOC的設計理念。所謂依賴注入(Dependency Injection),即組件之間的依賴關系由容器在應用系統運行期來決定,也就是由容器動態地將某種依賴關系的目標對象實例注入到應用系統中的各個關聯的組件之中。組件不做定位查詢,只提供普通的Java方法讓容器去決定依賴關系。 ### 依賴注入的基本原則 * 依賴注入的基本原則是:應用組件不應該負責查找資源或者其他依賴的協作對象。配置對象的工作應該由IOC容器負責,“查找資源”的邏輯應該從應用組件的代碼中抽取出來,交給IOC容器負責。容器全權負責組件的裝配,它會把符合依賴關系的對象通過屬性(JavaBean中的setter)或者是構造器傳遞給需要的對象。 ### 依賴注入有什么優勢 * 依賴注入之所以更流行是因為它是一種更可取的方式:讓容器全權負責依賴查詢,受管組件只需要暴露JavaBean的setter方法或者帶參數的構造器或者接口,使容器可以在初始化時組裝對象的依賴關系。其與依賴查找方式相比,主要優勢為: * 查找定位操作與應用代碼完全無關。 * 不依賴于容器的API,可以很容易地在任何容器以外使用應用對象。 * 不需要特殊的接口,絕大多數對象可以做到完全不必依賴容器。 ### 有哪些不同類型的依賴注入實現方式? * 依賴注入是時下最流行的IOC實現方式,依賴注入分為接口注入(Interface Injection),Setter方法注入(Setter Injection)和構造器注入(Constructor Injection)三種方式。其中接口注入由于在靈活性和易用性比較差,現在從Spring4開始已被廢棄。 * **構造器依賴注入**:構造器依賴注入通過容器觸發一個類的構造器來實現的,該類有一系列參數,每個參數代表一個對其他類的依賴。 * **Setter方法注入**:Setter方法注入是容器通過調用無參構造器或無參static工廠 方法實例化bean之后,調用該bean的setter方法,即實現了基于setter的依賴注入。 ### 構造器依賴注入和 Setter方法注入的區別 > | **構造函數注入** | **setter** **注入** | > | --- | --- | > | 沒有部分注入 | 有部分注入 | > | 不會覆蓋 setter 屬性 | 會覆蓋 setter 屬性 | > | 任意修改都會創建一個新實例 | 任意修改不會創建一個新實例 | > | 適用于設置很多屬性 | 適用于設置少量屬性 | * 兩種依賴方式都可以使用,構造器注入和Setter方法注入。最好的解決方案是用構造器參數實現強制依賴,setter方法實現可選依賴。 ## Spring Beans ### 什么是Spring beans? * Spring beans 是那些形成Spring應用的主干的java對象。它們被Spring IOC容器初始化,裝配,和管理。這些beans通過容器中配置的元數據創建。比如,以XML文件中 的形式定義。 ### 一個 Spring Bean 定義 包含什么? * 一個Spring Bean 的定義包含容器必知的所有配置元數據,包括如何創建一個bean,它的生命周期詳情及它的依賴。 ### 如何給Spring 容器提供配置元數據?Spring有幾種配置方式 * 這里有三種重要的方法給Spring 容器提供配置元數據。 * XML配置文件。 * 基于注解的配置。 * 基于java的配置。 ### Spring配置文件包含了哪些信息 * Spring配置文件是個XML 文件,這個文件包含了類信息,描述了如何配置它們,以及如何相互調用。 ### Spring基于xml注入bean的幾種方式 1. Set方法注入; 2. 構造器注入: 1. 通過index設置參數的位置; 2. 通過type設置參數類型; 3. 靜態工廠注入; 4. 實例工廠; ### 你怎樣定義類的作用域? * 當定義一個 在Spring里,我們還能給這個bean聲明一個作用域。它可以通過bean 定義中的scope屬性來定義。如,當Spring要在需要的時候每次生產一個新的bean實例,bean的scope屬性被指定為prototype。另一方面,一個bean每次使用的時候必須返回同一個實例,這個bean的scope 屬性 必須設為 singleton。 ### 解釋Spring支持的幾種bean的作用域 **Spring框架支持以下五種bean的作用域:** * **singleton :** bean在每個Spring ioc 容器中只有一個實例。 * **prototype**:一個bean的定義可以有多個實例。 * **request**:每次http請求都會創建一個bean,該作用域僅在基于web的Spring ApplicationContext情形下有效。 * **session**:在一個HTTP Session中,一個bean定義對應一個實例。該作用域僅在基于web的Spring ApplicationContext情形下有效。 * **global-session**:在一個全局的HTTP Session中,一個bean定義對應一個實例。該作用域僅在基于web的Spring ApplicationContext情形下有效。 **注意:** 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因為頻繁創建和銷毀 bean 會帶來很大的性能開銷。 ### Spring框架中的單例bean是線程安全的嗎? * 不是,Spring框架中的單例bean不是線程安全的。 * spring 中的 bean 默認是單例模式,spring 框架并沒有對單例 bean 進行多線程的封裝處理。 * 實際上大部分時候 spring bean 無狀態的(比如 dao 類),所有某種程度上來說 bean 也是安全的,但如果 bean 有狀態的話(比如 view model 對象),那就要開發者自己去保證線程安全了,最簡單的就是改變 bean 的作用域,把“singleton”變更為“prototype”,這樣請求 bean 相當于 new Bean()了,所以就可以保證線程安全了。 * 有狀態就是有數據存儲功能。 * 無狀態就是不會保存數據。 ### Spring如何處理線程并發問題? * 在一般情況下,只有無狀態的Bean才可以在多線程環境下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用域,因為Spring對一些Bean中非線程安全狀態采用ThreadLocal進行處理,解決線程安全問題。 * ThreadLocal和線程同步機制都是為了解決多線程中相同變量的訪問沖突問題。同步機制采用了“時間換空間”的方式,僅提供一份變量,不同的線程在訪問前需要獲取鎖,沒獲得鎖的線程則需要排隊。而ThreadLocal采用了“空間換時間”的方式。 * ThreadLocal會為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。 ### 解釋Spring框架中bean的生命周期 * 在傳統的Java應用中,bean的生命周期很簡單。使用Java關鍵字new進行bean實例化,然后該bean就可以使用了。一旦該bean不再被使用,則由Java自動進行垃圾回收。相比之下,Spring容器中的bean的生命周期就顯得相對復雜多了。正確理解Spring bean的生命周期非常重要,因為你或許要利用Spring提供的擴展點來自定義bean的創建過程。下圖展示了bean裝載到Spring應用上下文中的一個典型的生命周期過程。 ![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/4/13/1717443ebe68c24f~tplv-t2oaga2asx-watermark.awebp) * bean在Spring容器中從創建到銷毀經歷了若干階段,每一階段都可以針對Spring如何管理bean進行個性化定制。 * 正如你所見,在bean準備就緒之前,bean工廠執行了若干啟動步驟。 **我們對上圖進行詳細描述:** * Spring對bean進行實例化; * Spring將值和bean的引用注入到bean對應的屬性中; * 如果bean實現了BeanNameAware接口,Spring將bean的ID傳遞給setBean-Name()方法; * 如果bean實現了BeanFactoryAware接口,Spring將調用setBeanFactory()方法,將BeanFactory容器實例傳入; * 如果bean實現了ApplicationContextAware接口,Spring將調用setApplicationContext()方法,將bean所在的應用上下文的引用傳入進來; * 如果bean實現了BeanPostProcessor接口,Spring將調用它們的post-ProcessBeforeInitialization()方法; * 如果bean實現了InitializingBean接口,Spring將調用它們的after-PropertiesSet()方法。類似地,如果bean使用initmethod聲明了初始化方法,該方法也會被調用; * 如果bean實現了BeanPostProcessor接口,Spring將調用它們的post-ProcessAfterInitialization()方法; * 此時,bean已經準備就緒,可以被應用程序使用了,它們將一直駐留在應用上下文中,直到該應用上下文被銷毀; * 如果bean實現了DisposableBean接口,Spring將調用它的destroy()接口方法。同樣,如果bean使用destroy-method聲明了銷毀方法,該方法也會被調用。 `現在你已經了解了如何創建和加載一個Spring容器。但是一個空的容器并沒有太大的價值,在你把東西放進去之前,它里面什么都沒有。為了從Spring的DI(依賴注入)中受益,我們必須將應用對象裝配進Spring容器中。` ### 哪些是重要的bean生命周期方法? 你能重載它們嗎? * 有兩個重要的bean 生命周期方法,第一個是setup , 它是在容器加載bean的時候被調用。第二個方法是 teardown 它是在容器卸載類的時候被調用。 * bean 標簽有兩個重要的屬性(init-method和destroy-method)。用它們你可以自己定制初始化和注銷方法。它們也有相應的注解(@PostConstruct和@PreDestroy)。 ### 什么是Spring的內部bean?什么是Spring inner beans? * 在Spring框架中,當一個bean僅被用作另一個bean的屬性時,它能被聲明為一個內部bean。內部bean可以用setter注入“屬性”和構造方法注入“構造參數”的方式來實現,內部bean通常是匿名的,它們的Scope一般是prototype。 ### 在 Spring中如何注入一個java集合? * Spring提供了以下四種集合類的配置元素(配置標簽): | | 該標簽用來裝配可重復的list值。 | | --- | --- | | | 該標簽用來裝配沒有重復的set值。 | | | 該標簽可用來注入鍵和值可以為任何類型的鍵值對。 | | | 該標簽支持注入鍵和值都是字符串類型的鍵值對。 | ### 什么是bean裝配? * 裝配,或bean 裝配是指在Spring 容器中把bean組裝到一起,前提是容器需要知道bean的依賴關系,如何通過依賴注入來把它們裝配到一起。 ### 什么是bean的自動裝配? * 在Spring框架中,在配置文件中設定bean的依賴關系是一個很好的機制,Spring 容器能夠自動裝配相互合作的bean,這意味著容器不需要和配置,能通過Bean工廠自動處理bean之間的協作。這意味著 Spring可以通過向Bean Factory中注入的方式自動搞定bean之間的依賴關系。自動裝配可以設置在每個bean上,也可以設定在特定的bean上。 ### 解釋不同方式的自動裝配,spring 自動裝配 bean 有哪些方式? * 在spring中,對象無需自己查找或創建與其關聯的其他對象,由容器負責把需要相互協作的對象引用賦予各個對象,使用autowire來配置自動裝載模式。 * 在Spring框架xml配置中共有5種自動裝配: * no:默認的方式是不進行自動裝配的,通過手工設置ref屬性來進行裝配bean。 * byName:通過bean的名稱進行自動裝配,如果一個bean的 property 與另一bean 的name 相同,就進行自動裝配。 * byType:通過參數的數據類型進行自動裝配。 * constructor:利用構造函數進行裝配,并且構造函數的參數通過byType進行裝配。 * autodetect:自動探測,如果有構造方法,通過 construct的方式自動裝配,否則使用 byType的方式自動裝配。 ### 使用@Autowired注解自動裝配的過程是怎樣的? * 使用@Autowired注解來自動裝配指定的bean。在使用@Autowired注解之前需要在Spring配置文件進行配置,。 * 在啟動spring IOC時,容器自動裝載了一個AutowiredAnnotationBeanPostProcessor后置處理器,當容器掃描到@Autowied、@Resource或@Inject時,就會在IOC容器自動查找需要的bean,并裝配給該對象的屬性。在使用@Autowired時,首先在容器中查詢對應類型的bean: * 如果查詢結果剛好為一個,就將該bean裝配給@Autowired指定的數據; * 如果查詢的結果不止一個,那么@Autowired會根據名稱來查找; * 如果上述查找的結果為空,那么會拋出異常。解決方法時,使用required=false。 ### 自動裝配有哪些局限性? * 自動裝配的局限性是: * **重寫**:你仍需用 和 配置來定義依賴,意味著總要重寫自動裝配。 * **基本數據類型**:你不能自動裝配簡單的屬性,如基本數據類型,String字符串,和類。 * **模糊特性**:自動裝配不如顯式裝配精確,如果有可能,建議使用顯式裝配。 ### 你可以在Spring中注入一個null 和一個空字符串嗎? * 可以。 ## Spring注解 ### 什么是基于Java的Spring注解配置? 給一些注解的例子 * 基于Java的配置,允許你在少量的Java注解的幫助下,進行你的大部分Spring配置而非通過XML文件。 * 以@Configuration 注解為例,它用來標記類可以當做一個bean的定義,被Spring IOC容器使用。 * 另一個例子是@Bean注解,它表示此方法將要返回一個對象,作為一個bean注冊進Spring應用上下文。 ~~~ @Configuration public class StudentConfig { @Bean public StudentBean myStudent() { return new StudentBean(); } } 復制代碼 ~~~ ### 怎樣開啟注解裝配? * 注解裝配在默認情況下是不開啟的,為了使用注解裝配,我們必須在Spring配置文件中配置 `<context:annotation-config/>`元素。 ### @Component, @Controller, @Repository, @Service 有何區別? * @Component:這將 java 類標記為 bean。它是任何 Spring 管理組件的通用構造型。spring 的組件掃描機制現在可以將其拾取并將其拉入應用程序環境中。 * @Controller:這將一個類標記為 Spring Web MVC 控制器。標有它的 Bean 會自動導入到 IOC 容器中。 * @Service:此注解是組件注解的特化。它不會對 @Component 注解提供任何其他行為。您可以在服務層類中使用 @Service 而不是 @Component,因為它以更好的方式指定了意圖。 * @Repository:這個注解是具有類似用途和功能的 @Component 注解的特化。它為 DAO 提供了額外的好處。它將 DAO 導入 IOC 容器,并使未經檢查的異常有資格轉換為 Spring DataAccessException。 ### @Required 注解有什么作用 * 這個注解表明bean的屬性必須在配置的時候設置,通過一個bean定義的顯式的屬性值或通過自動裝配,若@Required注解的bean屬性未被設置,容器將拋出BeanInitializationException。示例: ~~~ public class Employee { private String name; @Required public void setName(String name){ this.name=name; } public string getName(){ return name; } } 復制代碼 ~~~ ### @Autowired 注解有什么作用 * @Autowired默認是按照類型裝配注入的,默認情況下它要求依賴對象必須存在(可以設置它required屬性為false)。@Autowired 注解提供了更細粒度的控制,包括在何處以及如何完成自動裝配。它的用法和@Required一樣,修飾setter方法、構造器、屬性或者具有任意名稱和/或多個參數的PN方法。 ~~~ public class Employee { private String name; @Autowired public void setName(String name) { this.name=name; } public string getName(){ return name; } } 復制代碼 ~~~ ### @Autowired和@Resource之間的區別 * @Autowired和@Resource可用于:構造函數、成員變量、Setter方法 * @Autowired和@Resource之間的區別在于 * @Autowired默認是按照類型裝配注入的,默認情況下它要求依賴對象必須存在(可以設置它required屬性為false)。 * @Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照類型來裝配注入。 ### @Qualifier 注解有什么作用 * 當您創建多個相同類型的 bean 并希望僅使用屬性裝配其中一個 bean 時,您可以使用@Qualifier 注解和 @Autowired 通過指定應該裝配哪個確切的 bean 來消除歧義。 ### @RequestMapping 注解有什么用? * @RequestMapping 注解用于將特定 HTTP 請求方法映射到將處理相應請求的控制器中的特定類/方法。此注釋可應用于兩個級別: * 類級別:映射請求的 URL * 方法級別:映射 URL 以及 HTTP 請求方法 ## Spring數據訪問 ### 解釋對象/關系映射集成模塊 * Spring 通過提供ORM模塊,支持我們在直接JDBC之上使用一個對象/關系映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 MyBatis,JPA,TopLink,JDO,OJB等待 。Spring的事務管理同樣支持以上所有ORM框架及JDBC。 ### 在Spring框架中如何更有效地使用JDBC? * 使用Spring JDBC 框架,資源管理和錯誤處理的代價都會被減輕。所以開發者只需寫statements 和 queries從數據存取數據,JDBC也可以在Spring框架提供的模板類的幫助下更有效地被使用,這個模板叫JdbcTemplate ### 解釋JDBC抽象和DAO模塊 * 通過使用JDBC抽象和DAO模塊,保證數據庫代碼的簡潔,并能避免數據庫資源錯誤關閉導致的問題,它在各種不同的數據庫的錯誤信息之上,提供了一個統一的異常訪問層。它還利用Spring的AOP 模塊給Spring應用中的對象提供事務管理服務。 ### spring DAO 有什么用? * Spring DAO(數據訪問對象) 使得 JDBC,Hibernate 或 JDO 這樣的數據訪問技術更容易以一種統一的方式工作。這使得用戶容易在持久性技術之間切換。它還允許您在編寫代碼時,無需考慮捕獲每種技術不同的異常。 ### spring JDBC API 中存在哪些類? * JdbcTemplate * SimpleJdbcTemplate * NamedParameterJdbcTemplate * SimpleJdbcInsert * SimpleJdbcCall ### JdbcTemplate是什么 * JdbcTemplate 類提供了很多便利的方法解決諸如把數據庫數據轉變成基本數據類型或對象,執行寫好的或可調用的數據庫操作語句,提供自定義的數據錯誤處理。 ### 使用Spring通過什么方式訪問Hibernate?使用 Spring 訪問 Hibernate 的方法有哪些? * 在Spring中有兩種方式訪問Hibernate: * 使用 Hibernate 模板和回調進行控制反轉 * 擴展 HibernateDAOSupport 并應用 AOP 攔截器節點 ### 如何通過HibernateDaoSupport將Spring和Hibernate結合起來? * 用Spring的 SessionFactory 調用 LocalSessionFactory。集成過程分三步: * 配置the Hibernate SessionFactory * 繼承HibernateDaoSupport實現一個DAO * 在AOP支持的事務中裝配 ### Spring支持的事務管理類型, spring 事務實現方式有哪些? * Spring支持兩種類型的事務管理: * **編程式事務管理**:這意味你通過編程的方式管理事務,給你帶來極大的靈活性,但是難維護。 * **聲明式事務管理**:這意味著你可以將業務代碼和事務管理分離,你只需用注解和XML配置來管理事務。 ### Spring事務的實現方式和實現原理 * Spring事務的本質其實就是數據庫對事務的支持,沒有數據庫的事務支持,spring是無法提供事務功能的。真正的數據庫層的事務提交和回滾是通過binlog或者redo log實現的。 ### 說一下Spring的事務傳播行為 spring事務的傳播行為說的是,當多個事務同時存在的時候,spring如何處理這些事務的行為。 > ① PROPAGATION\_REQUIRED:如果當前沒有事務,就創建一個新事務,如果當前存在事務,就加入該事務,該設置是最常用的設置。 > > ② PROPAGATION\_SUPPORTS:支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就以非事務執行。 > > ③ PROPAGATION\_MANDATORY:支持當前事務,如果當前存在事務,就加入該事務,如果當前不存在事務,就拋出異常。 > > ④ PROPAGATION\_REQUIRES\_NEW:創建新事務,無論當前存不存在事務,都創建新事務。 > > ⑤ PROPAGATION\_NOT\_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 > > ⑥ PROPAGATION\_NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。 > > ⑦ PROPAGATION\_NESTED:如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則按REQUIRED屬性執行。 ### 說一下 spring 的事務隔離? * spring 有五大隔離級別,默認值為 ISOLATION\_DEFAULT(使用數據庫的設置),其他四個隔離級別和數據庫的隔離級別一致: 1. ISOLATION\_DEFAULT:用底層數據庫的設置隔離級別,數據庫設置的是什么我就用什么; 2. ISOLATION\_READ\_UNCOMMITTED:未提交讀,最低隔離級別、事務未提交前,就可被其他事務讀取(會出現幻讀、臟讀、不可重復讀); 3. ISOLATION\_READ\_COMMITTED:提交讀,一個事務提交后才能被其他事務讀取到(會造成幻讀、不可重復讀),SQL server 的默認級別; 4. ISOLATION\_REPEATABLE\_READ:可重復讀,保證多次讀取同一個數據時,其值都和事務開始時候的內容是一致,禁止讀取到別的事務未提交的數據(會造成幻讀),MySQL 的默認級別; 5. ISOLATION\_SERIALIZABLE:序列化,代價最高最可靠的隔離級別,該隔離級別能防止臟讀、不可重復讀、幻讀。 * **臟讀** :表示一個事務能夠讀取另一個事務中還未提交的數據。比如,某個事務嘗試插入記錄 A,此時該事務還未提交,然后另一個事務嘗試讀取到了記錄 A。 * **不可重復讀** :是指在一個事務內,多次讀同一數據。 * **幻讀** :指同一個事務內多次查詢返回的結果集不一樣。比如同一個事務 A 第一次查詢時候有 n 條記錄,但是第二次同等條件下查詢卻有 n+1 條記錄,這就好像產生了幻覺。發生幻讀的原因也是另外一個事務新增或者刪除或者修改了第一個事務結果集里面的數據,同一個記錄的數據內容被修改了,所有數據行的記錄就變多或者變少了。 ### Spring框架的事務管理有哪些優點? * 為不同的事務API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一個不變的編程模式。 * 為編程式事務管理提供了一套簡單的API而不是一些復雜的事務API * 支持聲明式事務管理。 * 和Spring各種數據訪問抽象層很好得集成。 ### 你更傾向用那種事務管理類型? * 大多數Spring框架的用戶選擇聲明式事務管理,因為它對應用代碼的影響最小,因此更符合一個無侵入的輕量級容器的思想。聲明式事務管理要優于編程式事務管理,雖然比編程式事務管理(這種方式允許你通過代碼控制事務)少了一點靈活性。唯一不足地方是,最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別。 ## Spring面向切面編程(AOP) ### 什么是AOP * OOP(Object-Oriented Programming)面向對象編程,允許開發者定義縱向的關系,但并適用于定義橫向的關系,導致了大量代碼的重復,而不利于各個模塊的重用。 * AOP(Aspect-Oriented Programming),一般稱為面向切面編程,作為面向對象的一種補充,用于將那些與業務無關,但卻對多個對象產生影響的公共行為和邏輯,抽取并封裝為一個可重用的模塊,這個模塊被命名為“切面”(Aspect),減少系統中的重復代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。可用于權限認證、日志、事務處理等。 ### Spring AOP and AspectJ AOP 有什么區別?AOP 有哪些實現方式? * AOP實現的關鍵在于 代理模式,AOP代理主要分為靜態代理和動態代理。靜態代理的代表為AspectJ;動態代理則以Spring AOP為代表。 * (1)AspectJ是靜態代理的增強,所謂靜態代理,就是AOP框架會在編譯階段生成AOP代理類,因此也稱為編譯時增強,他會在編譯階段將AspectJ(切面)織入到Java字節碼中,運行的時候就是增強之后的AOP對象。 * (2)Spring AOP使用的動態代理,所謂的動態代理就是說AOP框架不會去修改字節碼,而是每次運行時在內存中臨時為方法生成一個AOP對象,這個AOP對象包含了目標對象的全部方法,并且在特定的切點做了增強處理,并回調原對象的方法。 ### JDK動態代理和CGLIB動態代理的區別 * Spring AOP中的動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理: * JDK動態代理只提供接口的代理,不支持類的代理。核心InvocationHandler接口和Proxy類,InvocationHandler 通過invoke()方法反射來調用目標類中的代碼,動態地將橫切邏輯和業務編織在一起;接著,Proxy利用 InvocationHandler動態創建一個符合某一接口的的實例, 生成目標類的代理對象。 * 如果代理類沒有實現 InvocationHandler 接口,那么Spring AOP會選擇使用CGLIB來動態代理目標類。CGLIB(Code Generation Library),是一個代碼生成的類庫,可以在運行時動態的生成指定類的一個子類對象,并覆蓋其中特定方法并添加增強代碼,從而實現AOP。CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記為final,那么它是無法使用CGLIB做動態代理的。 * 靜態代理與動態代理區別在于生成AOP代理對象的時機不同,相對來說AspectJ的靜態代理方式具有更好的性能,但是AspectJ需要特定的編譯器進行處理,而Spring AOP則無需特定的編譯器處理。 > InvocationHandler 的 invoke(Object proxy,Method method,Object\[\] args):proxy是最終生成的代理實例; method 是被代理目標實例的某個具體方法; args 是被代理目標實例某個方法的具體入參, 在方法反射調用時使用。 ### 解釋一下Spring AOP里面的幾個名詞 * (1)切面(Aspect):切面是通知和切點的結合。通知和切點共同定義了切面的全部內容。 在Spring AOP中,切面可以使用通用類(基于模式的風格) 或者在普通類中以 @AspectJ 注解來實現。 * (2)連接點(Join point):指方法,在Spring AOP中,一個連接點 總是 代表一個方法的執行。 應用可能有數以千計的時機應用通知。這些時機被稱為連接點。連接點是在應用執行過程中能夠插入切面的一個點。這個點可以是調用方法時、拋出異常時、甚至修改一個字段時。切面代碼可以利用這些點插入到應用的正常流程之中,并添加新的行為。 * (3)通知(Advice):在AOP術語中,切面的工作被稱為通知。 * (4)切入點(Pointcut):切點的定義會匹配通知所要織入的一個或多個連接點。我們通常使用明確的類和方法名稱,或是利用正則表達式定義所匹配的類和方法名稱來指定這些切點。 * (5)引入(Introduction):引入允許我們向現有類添加新方法或屬性。 * (6)目標對象(Target Object): 被一個或者多個切面(aspect)所通知(advise)的對象。它通常是一個代理對象。也有人把它叫做 被通知(adviced) 對象。 既然Spring AOP是通過運行時代理實現的,這個對象永遠是一個 被代理(proxied) 對象。 * (7)織入(Weaving):織入是把切面應用到目標對象并創建新的代理對象的過程。在目標對象的生命周期里有多少個點可以進行織入: * 編譯期:切面在目標類編譯時被織入。AspectJ的織入編譯器是以這種方式織入切面的。 * 類加載期:切面在目標類加載到JVM時被織入。需要特殊的類加載器,它可以在目標類被引入應用之前增強該目標類的字節碼。AspectJ5的加載時織入就支持以這種方式織入切面。 * 運行期:切面在應用運行的某個時刻被織入。一般情況下,在織入切面時,AOP容器會為目標對象動態地創建一個代理對象。SpringAOP就是以這種方式織入切面。 ### Spring在運行時通知對象 * 通過在代理類中包裹切面,Spring在運行期把切面織入到Spring管理的bean中。代理封裝了目標類,并攔截被通知方法的調用,再把調用轉發給真正的目標bean。當代理攔截到方法調用時,在調用目標bean方法之前,會執行切面邏輯。 * 直到應用需要被代理的bean時,Spring才創建代理對象。如果使用的是ApplicationContext的話,在ApplicationContext從BeanFactory中加載所有bean的時候,Spring才會創建被代理的對象。因為Spring運行時才創建代理對象,所以我們不需要特殊的編譯器來織入SpringAOP的切面。 ### Spring只支持方法級別的連接點 * 因為Spring基于動態代理,所以Spring只支持方法連接點。Spring缺少對字段連接點的支持,而且它不支持構造器連接點。方法之外的連接點攔截功能,我們可以利用Aspect來補充。 ### 在Spring AOP 中,關注點和橫切關注的區別是什么?在 spring aop 中 concern 和 cross-cutting concern 的不同之處 * 關注點(concern)是應用中一個模塊的行為,一個關注點可能會被定義成一個我們想實現的一個功能。 * 橫切關注點(cross-cutting concern)是一個關注點,此關注點是整個應用都會使用的功能,并影響整個應用,比如日志,安全和數據傳輸,幾乎應用的每個模塊都需要的功能。因此這些都屬于橫切關注點。 ### Spring通知有哪些類型? * 在AOP術語中,切面的工作被稱為通知,實際上是程序執行時要通過SpringAOP框架觸發的代碼段。 * Spring切面可以應用5種類型的通知: 1. 前置通知(Before):在目標方法被調用之前調用通知功能; 2. 后置通知(After):在目標方法完成之后調用通知,此時不會關心方法的輸出是什么; 3. 返回通知(After-returning ):在目標方法成功執行之后調用通知; 4. 異常通知(After-throwing):在目標方法拋出異常后調用通知; 5. 環繞通知(Around):通知包裹了被通知的方法,在被通知的方法調用之前和調用之后執行自定義的行為。 ### 什么是切面 Aspect? * aspect 由 pointcount 和 advice 組成,切面是通知和切點的結合。 它既包含了橫切邏輯的定義, 也包括了連接點的定義. Spring AOP 就是負責實施切面的框架, 它將切面所定義的橫切邏輯編織到切面所指定的連接點中. AOP 的工作重心在于如何將增強編織目標對象的連接點上, 這里包含兩個工作: * 如何通過 pointcut 和 advice 定位到特定的 joinpoint 上 * 如何在 advice 中編寫切面代碼. * 可以簡單地認為, 使用 @Aspect 注解的類就是切面. ![在這里插入圖片描述](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/4/13/1717443ebe76a748~tplv-t2oaga2asx-watermark.awebp) ### 解釋基于XML Schema方式的切面實現 * 在這種情況下,切面由常規類以及基于XML的配置實現。 ### 解釋基于注解的切面實現 * 在這種情況下(基于@AspectJ的實現),涉及到的切面聲明的風格與帶有java5標注的普通java類一致。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看