<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                在企業應用軟件開發中,Java 是毫無爭議的主流語言,開放的 Java EE 規范和強大的開源框架功不可沒,其中 Spring 毫無疑問已經成為企業軟件開發的事實標準之一。今天這一講,我將補充 Spring 相關的典型面試問題,并談談其部分設計細節。 今天我要問你的問題是,談談 Spring Bean 的生命周期和作用域? ## 典型回答 Spring Bean 生命周期比較復雜,可以分為創建和銷毀兩個過程。 首先,創建 Bean 會經過一系列的步驟,主要包括: * 實例化 Bean 對象。 * 設置 Bean 屬性。 * 如果我們通過各種 Aware 接口聲明了依賴關系,則會注入 Bean 對容器基礎設施層面的依賴。具體包括 BeanNameAware、BeanFactoryAware 和 ApplicationContextAware,分別會注入 Bean ID、Bean Factory 或者 ApplicationContext。 * 調用 BeanPostProcessor 的前置初始化方法 postProcessBeforeInitialization。 * 如果實現了 InitializingBean 接口,則會調用 afterPropertiesSet 方法。 * 調用 Bean 自身定義的 init 方法。 * 調用 BeanPostProcessor 的后置初始化方法 postProcessAfterInitialization。 * 創建過程完畢。 你可以參考下面示意圖理解這個具體過程和先后順序。 ![](https://img.kancloud.cn/3a/51/3a51f06f56b905b8fbf1661359e1727e_462x571.png) 第二,Spring Bean 的銷毀過程會依次調用 DisposableBean 的 destroy 方法和 Bean 自身定制的 destroy 方法。 Spring Bean 有五個作用域,其中最基礎的有下面兩種: * Singleton,這是 Spring 的默認作用域,也就是為每個 IOC 容器創建唯一的一個 Bean 實例。 * Prototype,針對每個 getBean 請求,容器都會單獨創建一個 Bean 實例。 從 Bean 的特點來看,Prototype 適合有狀態的 Bean,而 Singleton 則更適合無狀態的情況。另外,使用 Prototype 作用域需要經過仔細思考,畢竟頻繁創建和銷毀 Bean 是有明顯開銷的。 如果是 Web 容器,則支持另外三種作用域: * Request,為每個 HTTP 請求創建單獨的 Bean 實例。 * Session,很顯然 Bean 實例的作用域是 Session 范圍。 * GlobalSession,用于 Portlet 容器,因為每個 Portlet 有單獨的 Session,GlobalSession 提供一個全局性的 HTTP Session。 ## 考點分析 今天我選取的是一個入門性質的高頻 Spring 面試題目,我認為相比于記憶題目典型回答里的細節步驟,理解和思考 Bean 生命周期所體現出來的 Spring 設計和機制更有意義。 你能看到,Bean 的生命周期是完全被容器所管理的,從屬性設置到各種依賴關系,都是容器負責注入,并進行各個階段其他事宜的處理,Spring 容器為應用開發者定義了清晰的生命周期溝通界面。 如果從具體 API 設計和使用技巧來看,還記得我在[專欄第 13 講](http://time.geekbang.org/column/article/8471)提到過的 Marker Interface 嗎,Aware 接口就是個典型應用例子,Bean 可以實現各種不同 Aware 的子接口,為容器以 Callback 形式注入依賴對象提供了統一入口。 言歸正傳,還是回到 Spring 的學習和面試。關于 Spring,也許一整本書都無法完整涵蓋其內容,專欄里我會有限地補充: * Spring 的基礎機制。 * Spring 框架的涵蓋范圍。 * Spring AOP 自身設計的一些細節,前面[第 24 講](http://time.geekbang.org/column/article/10076)偏重于底層實現原理,這樣還不夠全面,畢竟不管是動態代理還是字節碼操縱,都還只是基礎,更需要 Spring 層面對切面編程的支持。 ## 知識擴展 首先,我們先來看看 Spring 的基礎機制,至少你需要理解下面兩個基本方面。 * 控制反轉(Inversion of Control),或者也叫依賴注入(Dependency Injection),廣泛應用于 Spring 框架之中,可以有效地改善了模塊之間的緊耦合問題。 從 Bean 創建過程可以看到,它的依賴關系都是由容器負責注入,具體實現方式包括帶參數的構造函數、setter 方法或者[AutoWired](https://docs.spring.io/spring-framework/docs/5.0.3.RELEASE/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html)方式實現。 * AOP,我們已經在前面接觸過這種切面編程機制,Spring 框架中的事務、安全、日志等功能都依賴于 AOP 技術,下面我會進一步介紹。 第二,Spring 到底是指什么? 我前面談到的 Spring,其實是狹義的[Spring Framework](https://github.com/spring-projects/spring-framework/blob/67ea4b3a050af3db5545f58ff85a0d132ee91c2a/spring-aop/src/main/java/org/aopalliance/aop/Advice.java),其內部包含了依賴注入、事件機制等核心模塊,也包括事務、O/R Mapping 等功能組成的數據訪問模塊,以及 Spring MVC 等 Web 框架和其他基礎組件。 廣義上的 Spring 已經成為了一個龐大的生態系統,例如: * Spring Boot,通過整合通用實踐,更加自動、智能的依賴管理等,Spring Boot 提供了各種典型應用領域的快速開發基礎,所以它是以應用為中心的一個框架集合。 * Spring Cloud,可以看作是在 Spring Boot 基礎上發展出的更加高層次的框架,它提供了構建分布式系統的通用模式,包含服務發現和服務注冊、分布式配置管理、負載均衡、分布式診斷等各種子系統,可以簡化微服務系統的構建。 * 當然,還有針對特定領域的 Spring Security、Spring Data 等。 上面的介紹比較籠統,針對這么多內容,如果將目標定得太過寬泛,可能就迷失在 Spring 生態之中,我建議還是深入你當前使用的模塊,如 Spring MVC。并且,從整體上把握主要前沿框架(如 Spring Cloud)的應用范圍和內部設計,至少要了解主要組件和具體用途,畢竟如何構建微服務等,已經逐漸成為 Java 應用開發面試的熱點之一。 第三,我們來探討一下更多有關 Spring AOP 自身設計和實現的細節。 先問一下自己,我們為什么需要切面編程呢? 切面編程落實到軟件工程其實是為了更好地模塊化,而不僅僅是為了減少重復代碼。通過 AOP 等機制,我們可以把橫跨多個不同模塊的代碼抽離出來,讓模塊本身變得更加內聚,進而業務開發者可以更加專注于業務邏輯本身。從迭代能力上來看,我們可以通過切面的方式進行修改或者新增功能,這種能力不管是在問題診斷還是產品能力擴展中,都非常有用。 在之前的分析中,我們已經分析了 AOP Proxy 的實現原理,簡單回顧一下,它底層是基于 JDK 動態代理或者 cglib 字節碼操縱等技術,運行時動態生成被調用類型的子類等,并實例化代理對象,實際的方法調用會被代理給相應的代理對象。但是,這并沒有解釋具體在 AOP 設計層面,什么是切面,如何定義切入點和切面行為呢? Spring AOP 引入了其他幾個關鍵概念: * Aspect,通常叫作方面,它是跨不同 Java 類層面的橫切性邏輯。在實現形式上,既可以是 XML 文件中配置的普通類,也可以在類代碼中用“@Aspect”注解去聲明。在運行時,Spring 框架會創建類似[Advisor](https://github.com/spring-projects/spring-framework/blob/master/spring-aop/src/main/java/org/springframework/aop/Advisor.java)來指代它,其內部會包括切入的時機(Pointcut)和切入的動作(Advice)。 * Join Point,它是 Aspect 可以切入的特定點,在 Spring 里面只有方法可以作為 Join Point。 * [](https://github.com/spring-projects/spring-framework/blob/67ea4b3a050af3db5545f58ff85a0d132ee91c2a/spring-aop/src/main/java/org/aopalliance/aop/Advice.java)[Advice](https://github.com/spring-projects/spring-framework/blob/67ea4b3a050af3db5545f58ff85a0d132ee91c2a/spring-aop/src/main/java/org/aopalliance/aop/Advice.java),它定義了切面中能夠采取的動作。如果你去看 Spring 源碼,就會發現 Advice、Join Point 并沒有定義在 Spring 自己的命名空間里,這是因為他們是源自[AOP 聯盟](http://aopalliance.sourceforge.net/),可以看作是 Java 工程師在 AOP 層面溝通的通用規范。 Java 核心類庫中同樣存在類似代碼,例如 Java 9 中引入的 Flow API 就是 Reactive Stream 規范的最小子集,通過這種方式,可以保證不同產品直接的無縫溝通,促進了良好實踐的推廣。 具體的 Spring Advice 結構請參考下面的示意圖。 ![](https://img.kancloud.cn/5b/69/5b6955b4757c1a5fd0ecacdaf835e3ba_850x371.png) 其中,BeforeAdvice 和 AfterAdvice 包括它們的子接口是最簡單的實現。而 Interceptor 則是所謂的攔截器,用于攔截住方法(也包括構造器)調用事件,進而采取相應動作,所以 Interceptor 是覆蓋住整個方法調用過程的 Advice。通常將攔截器類型的 Advice 叫作 Around,在代碼中可以使用“@Around”來標記,或者在配置中使用“”。 如果從時序上來看,則可以參考下圖,理解具體發生的時機。 ![](https://img.kancloud.cn/85/20/85205c0c0ddcdafd2fad4ff5a53af0cb_479x309.png) * Pointcut,它負責具體定義 Aspect 被應用在哪些 Join Point,可以通過指定具體的類名和方法名來實現,或者也可以使用正則表達式來定義條件。 你可以參看下面的示意圖,來進一步理解上面這些抽象在邏輯上的意義。 ![](https://img.kancloud.cn/de/e9/dee96c33619d76d33281332bb3d2494a_475x413.png) * Join Point 僅僅是可利用的機會。 * Pointcut 是解決了切面編程中的 Where 問題,讓程序可以知道哪些機會點可以應用某個切面動作。 * 而 Advice 則是明確了切面編程中的 What,也就是做什么;同時通過指定 Before、After 或者 Around,定義了 When,也就是什么時候做。 在準備面試時,如果在實踐中使用過 AOP 是最好的,否則你可以選擇一個典型的 AOP 實例,理解具體的實現語法細節,因為在面試考察中也許會問到這些技術細節。 如果你有興趣深入內部,最好可以結合 Bean 生命周期,理解 Spring 如何解析 AOP 相關的注解或者配置項,何時何地使用到動態代理等機制。為了避免被龐雜的源碼弄暈,我建議你可以從比較精簡的測試用例作為一個切入點,如[CglibProxyTests](https://github.com/spring-projects/spring-framework/blob/da80502ea6ed4860f5bf7b668300644cdfe3bb5a/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java)。 另外,Spring 框架本身功能點非常多,AOP 并不是它所支持的唯一切面技術,它只能利用動態代理進行運行時編織,而不能進行編譯期的靜態編織或者類加載期編織。例如,在 Java 平臺上,我們可以使用 Java Agent 技術,在類加載過程中對字節碼進行操縱,比如修改或者替換方法實現等。在 Spring 體系中,如何做到類似功能呢?你可以使用 AspectJ,它具有更加全面的能力,當然使用也更加復雜。 今天我從一個常見的 Spring 面試題開始,淺談了 Spring 的基礎機制,探討了 Spring 生態范圍,并且補充分析了部分 AOP 的設計細節,希望對你有所幫助。 ## 一課一練 關于今天我們討論的題目你做到心中有數了嗎?今天的思考題是,請介紹一下 Spring 聲明式事務的實現機制,可以考慮將具體過程畫圖。
                  <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>

                              哎呀哎呀视频在线观看