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

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] 本文改編自[Spring的本質系列](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513179&idx=1&sn=772226a5be436a0d08197c335ddb52b8&scene=21#wechat_redirect) ## 對象的創建 面向對象的編程語言是用類 (Class) 來對現實世界進行抽象, 在運行時這些類會生成對象(Object)。當然單獨的對象沒有辦法完成復雜的業務,實際系統是由千千萬萬個對象組成的,而且他們需要相互協作。那么對象A入耳才能獲得對象B的引用呢? 最簡單的方法無非是,當對象A需要使用B的時候,才把它**new**出來。例如 ``` Apple a = new Apple(); ``` 后來業務更復雜了,就抽象出一個Fruit類,創建的對象都賦給fruit的一個實例。 ~~~ Fruit f1 = new Apple(); Fruit f2 = new Banana(); Fruit f3 = ...... ~~~ 這樣的代碼如果散落在各處,維護起來將會痛苦不堪。 那么也可以能出現如下的工廠類 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180811170653.png?picgo) 工廠模式,以及抽象工廠, Builder 模式提供的都是創建對象的方法。背后體現的都是**封裝變化**的思想 這些模式只是一些最佳實踐而已: 起了一個名稱、描述一下解決的問題、使用的范圍和場景,碼農們在項目中還得自己去編碼實現他們。 ## 解除依賴 一個訂單處理類,它會被定時調用: 查詢數據庫中訂單的處理情況, 必要時給下訂單的用戶發信。 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180811170846.png?picgo) 這里就需要很多類一起協作了,尤其是OrderProcessor,它依賴于OrderService和EmailService兩個服務,它獲取依賴的方式就是通過**單例方法** 但是如果你想對這個 process 方法進行單元測試,就會發現 * OrderService會從數據庫中取到Order的信息,需要確保數據庫中有數據,而且數據庫連接有問題。如果是數據連接Container(例如Tomcat)管理數據連接的,那么沒有Tomcat很難建立數據庫連接 * 這個 EmailService 真的會對外發郵件, 你可不想對真正的用戶發測試郵件,當然你可以修改數據庫,把郵件地址改成假的,但那樣很麻煩, 并且 EmailService 會拋出一堆錯誤來,很不爽 所有的這些障礙, 最終會導致脆弱的單元測試: **速度慢, 不可重復,需要手工干預,不能獨立運行。** 一個可行的辦法就是不在方法中直接調用OrderService和EmailService的getInstance()方法,而是通過setter方法傳進來。 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180811171246.png?picgo) 也就是構建一個假的 OrderService 和假的 EmailService 了。 例如 OrderService 的冒牌貨可以是 MockOrderService , 它可以返回你想要的任何 Order 對象, 而不是從數據庫取。MockEmailService 也不會真的發郵件, 而是把代碼中試圖發的郵件保存下來, 測試程序可以檢查是否正確。 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180811171447.png?picgo) 那么你會發現需要把OrderService和EmailService變成接口或者抽象類,這樣才能把Mock對象傳進來。 這其實也遵循了面向對象編程的另外一個要求:**對接口編程, 而不是對實現編程** ## Spring依賴注入 上面的代碼其實就是實現了一個依賴的注入,把兩個冒牌貨注入到業務類中 (通過 set 方法), 這個注入的過程是在一個測試類中通過代碼完成的。 既然能把冒牌貨注入進去, 那毫無疑問,肯定也能把一個正經的類安插進去, 因為 setter 方法接受的是接口,而不是具體類。 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180811171807.png?picgo) 用這種方式來處理對象之間的依賴, 會強迫你對接口編程, 好處顯而易見。 隨著系統復雜度的增長, 這樣的代碼會越來越多, 最后也會變得難于維護。 能不能把各個類之間的依賴關系統一維護呢? 能不能把系統做的更加靈活一點,用聲明的方式而不是用代碼的方式來描述依賴關系呢? 在 Java 世界里,如果想描述各種邏輯關系, XML 是不二之選: ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180811171901.png?picgo) 只有xml還不夠,還缺少一個解析器,假設為XmlAppContext * 解析xml,獲得各種元素 * 通過**Java反射**把各個bean的實例創建起來。com.coderising.OrderProcessor , OrderServiceImpl, EmailServiceImpl. * 通過**Java反射**調用OrderProcessor 的兩個方法:setOrderService(....) 和 setEmailService(...) 把 orderService , emailService 實例 注入進去。 應用程序使用起來就簡單了: ~~~ XmlAppContext ctx = new XmlAppContext("c:\\bean.xml"); OrderProcessor op = (OrderProcessor) ctx.getBean("order-processor"); op.process(); ~~~ pring 的處理方式和上面說的非常類似, 當然 Spring 處理了更多的細節,例如不僅僅是 setter 方法注入, 還可以構造函數注入,init 方法, destroy 方法等等, 基本思想是一致的。 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180811172152.png?picgo) 既然對象的創建過程和裝配過程都是 Spring 做的, 那 Spring 在這個過程中就可以玩很多把戲了, 比如對你的業務類做點字節碼級別的增強, 搞點 AOP 什么的, 這都不在話下了。 ## IoC vs DI 在好萊塢,把簡歷遞交給演藝公司后就只有回家等待。由演藝公司對整個娛樂項目完全控制,演員只能被動式的接受公司的差使, 在需要的環節中,完成自己的演出。 這和軟件開發有一定的相似性, 演員們就像一個個 Java Object, 最早的時候自己去創建自己所依賴的對象, 有了演藝公司(Spring 容器)的介入,所有的依賴關系都是演藝公司搞定的, 于是控制就翻轉了 Inversion of Control, 簡稱 IoC。 但是 IoC 這個詞不能讓人更加直觀和清晰的理解背后所代表的含義, 于是 Martin Flower 先生就創造了一個新詞 : 依賴注入 (Dependency Injection,簡稱 DI), 是不是更加貼切一點? ## AOP AOP(Aspect Oriented Programming)也就是面向切面編程 ### 問題來源 做系統設計的時候,非常重要的工作是把一個大系統分解,按照業務功能分解成一個一個低耦合、高內聚的模塊 分解后會發現有很多有趣的東西,比如下面的模塊是通用的。 * 日志 * 安全 * 性能 * 事務 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180815104526.png?picgo) 這些非功能需求,但他們是多個業務模塊都需要的,最簡單的方法是把這些通用的模塊寫好,然后寫業務模塊的時候去調用就可以了。但是這樣的話,日志、性能等無關的代碼可以把真正的業務代碼淹沒了。 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180815104541.png?picgo) ## 模板方法 用設計模式可以部分解決如上的問題。比如模板方法 在父類(BaseCommand)中把亂七八糟的非功能代碼都寫好,只留了一個口最長(抽象方法doBusiness)讓子類實現 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180815104746.png?picgo) 子類就只需要關注業務邏輯了。 ``` BaseCommand cmd = ... 獲得 PlaceOrderCommand 的實例... cmd.execute(); ``` 但是這種方案的巨大缺陷在于父類會定義一切,要執行那些非功能代碼,以什么順序執行等等。 子類只能無條件接受,完全沒有反抗余地 ## 裝飾者模式 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180815105242.png?picgo) 讓PlaceOrderCommand能打印日志,進行性能統計 ``` Command cmd = new LoggerDecorator( new PerformanceDecorator( new PlaceOrderCommand())); cmd.execute(); ``` 如果 PaymentCommand 只需要打印日志,裝飾一次就可以了 ``` Command cmd = new LoggerDecorator( new PaymentCommand()); cmd.execute(); ``` 可以使用任意數量的裝飾器,而且還可以以任意次序執行,非常的靈活 ## AOP 但是裝飾者有很多問題: * 一個處理日志、性能、事務的類為什么要實現業務接口(Command)呢? * 如果別的業務模塊,沒有實現Command接口,但是也想利用日志、性能、事務等,應該怎么辦呢? 最好把日志、安全、事務這些代碼與業務代碼隔離出來,因為他們的關注點與業務代碼的關注點完全不同。 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180815110232.png?picgo) 如果把這個業務功能看成一層層面包的話, 這些日志 / 安全 / 事務 像不像一個個 “切面”(Aspect) ? 如果我們能讓這些 “切面“能和業務獨立, 并且能夠非常靈活的“織入” 到業務方法中, 那就實現了面向切面編程(AOP)! ## 實現AOP 現在我們來實現 AOP 吧, 首先我們得有一個所謂的 “切面“類(Aspect), 這應該是一個普通的 java 類 , 不用實現什么“亂七八糟” 的接口。 ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180815110720.png?picgo) 我們對于 com.coderising 這個包中所有類的 execute 方法, 在方法調用之前,需要執行 Transaction.beginTx() 方法, 在調用之后, 需要執行 Transaction.commitTx() 方法。 “對于 com.coderising 這個包中所有類的 execute 方法” , 用一個時髦的詞來描述就是切入點(PointCut) , 它可以是一個方法或一組方法(可以通過通配符來支持,你懂的) ” 在方法調用之前 / 之后 , 需要執行 xxx“ , 用另外一個時髦的詞來描述就是通知(Advice) ![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180815110459.png?picgo) 注意:現在 Transaction 這個類和業務類在源代碼層次上沒有一點關系,完全隔離了。 但是Java是一門靜態的強類型語言, 代碼一旦寫好, 編譯成 java class 以后 ,可以在運行時通過反射(Reflection)來查看類的信息, 但是想對類進行修改非常困難。 而 AOP 要求的恰恰就是在不改變業務類的源代碼(其實大部分情況下你也拿不到)的情況下, 修改業務類的方法, 進行功能的增強,**就像上面給所有的業務類增加事務支持。** 目前有如下幾種技術: * 編譯的時候把日志、安全、事務等切面代碼與業務邏輯編譯到一起去。 * 運行的時候,業務類加載以后, 通過 Java 動態代理技術為業務類生產一個代理類, 把 “切面” 代碼放到代理類中, Java 動態代理要求業務類需要實現接口才行。 * 在運行期, 業務類加載以后, 動態的使用**字節碼**構建一個業務類的子類,將 “切面” 邏輯加入到子類當中去, CGLIB 就是這么做的。 Spring 采用的就是 (1) +(2) 的方式
                  <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>

                              哎呀哎呀视频在线观看