走進JavaWeb技術世界10:從JavaBean講到Spring

a724888發表於2019-08-16

微信公眾號【黃小斜】大廠程式設計師,網際網路行業新知,終身學習踐行者。關注後回覆「Java」、「Python」、「C++」、「大資料」、「機器學習」、「演算法」、「AI」、「Android」、「前端」、「iOS」、「考研」、「BAT」、「校招」、「筆試」、「面試」、「面經」、「計算機基礎」、「LeetCode」 等關鍵字可以獲取對應的免費學習資料。 


                      走進JavaWeb技術世界10:從JavaBean講到Spring

Java 帝國之Java bean (上)

轉自:   劉欣     2016-05-27

前言: 最近看到到spring 的bean 配置, 突然想到可能很多人不一定知道這個叫bean的東西的來龍去脈, 所以就寫個文章來講一下。

 

另外,上次出了開源中國 , 為了防止轉載以後我的公眾號資訊被故意刪除, 我在文章的內容中加上了一些 劉欣(微信公眾號:碼農翻身) 這樣的字樣, 可能會造成一些煩擾, 請見諒。 

我一手創立的Java帝國剛剛成立不久,便受到巨大的打擊, 我派出去佔領桌面開發的部隊幾乎全軍覆沒。 

 

情報說微軟的Visual Basic 和Borland的Delphi最近在宣傳什麼元件化開發, 難道就是這東西把我們搞垮了? 

 

劉欣(微信公眾號:碼農翻身)注:參見《 》和《 》

 

我趕緊買了一個Visual Basic 過來研究, 果然,這個傢伙確實是方便, 最讓我驚歎的是:它有一個視覺化編輯器 ! 

 

我只需要把一個元件(例如按鈕)拖拽到可一個表單上,  設定一下屬性 (顏色,字型), 再新增一個事件(onClick), 最後在onClick中寫點程式碼就搞定了 !

 

不僅如此,我自己也可以把我的程式碼按規範包裝成一個元件, 釋出出去讓別人使用。

 

我看著手下給我買來的《程式設計師大本營》光碟, 裡邊竟然包含了好幾千個這樣的元件, 有資料庫瀏覽元件, 計時器元件, 顏色選取元件, 甚至還有收發郵件的元件......

 

天哪, 這以後開發桌面程式豈不太簡單了 !

 

怪不得我的Java 被打得滿地找牙!

 

劉欣(微信公眾號:碼農翻身)注: 90年代末的程式設計師和學生估計都知道《程式設計師大本營》, 由csdn的創始人蔣濤製作。

 

我趕緊打電話給我的干將小碼哥 : 小碼啊, 你趕緊看看這個Visual Basic 和Delphi , 給你7天時間, 我們Java 也得趕緊搞一套這樣的東西出來。

 

小嗎毫不含糊, 三天就給我搞了一個東西出來: Java Bean API 規範 。

 

我翻開一看, 哇塞, 長達114頁 , 於是問他:“這是什麼東西? 我要的視覺化編輯器呢Visual Java 呢? ”

 

劉欣(微信公眾號:碼農翻身)注: 我下載瀏覽了java bean 的規範, 確實是114頁

 

他說: “老大, 我們是個開源的社群, 得充分利用大家的力量, 所以我沒有去做像VB和Delphi那樣的東西, 相反,我定義了一套規範, 只要大家按照這個規範做, 誰都可以用java 做出像VB那樣的視覺化開發工具出來。”

 

“那你說說這個java bean 到底是什麼規範?”我問。

 

“首先,一個java bean 其實就是一個普通的java 類, 但我們對這個類有些要求: 

 

1. 這個類需要是public 的, 然後需要有個無引數的建構函式

 

2. 這個類的屬性應該是private 的, 透過setXXX()和getXXX()來訪問

 

3. 這個類需要能支援“事件”, 例如addXXXXListener(XXXEvent e),  事件可以是Click事件,Keyboard事件等等, 當然我們們也支援自定義的事件。 

 

4. 我們得提供一個所謂的自省/反射機制, 這樣能在執行時檢視java bean 的各種資訊“

 

5. 這個類應該是可以序列化的, 即可以把bean的狀態儲存的硬碟上, 以便以後來恢復。 

 

“這些要求看起來也沒啥啊,對程式設計師來說,不就是個普通的java 類嗎? 到底該怎麼用? ” 

 

“我們幻想一下,假設我們的Java bean 大行其道了, 有個使用者在用一個Visual Java Builder  這樣的視覺化開發工具, 當他用這個工具建立應用的時候, 可以選擇一個叫JButton的元件, 加到一個表單上, 此時Visual Java Builder 就需要把這JButton的類透過反射給new 出來, 所以就需要一個無引數的建構函式了。”

 

“如果使用者想去設定一下這個JButton的屬性,Visual Java Builder 就需要先用自省/反射來獲取這個JButton有哪些屬性(透過getter/setter), 拿到以後就可以給使用者顯示一個 屬性清單 了, 例如背景色, 字型 等等。使用者看到後就可以設定背景色和字型了,  此時Visual Java Builder 在內部就需要呼叫這個Bean的setBackgroundCorlor()/setFont() 等方法, 這就是所謂的setXXXX()方法。”

 

“如果使用者想對這個JButton程式設計, Visual Java Builder 還是透過自省/反射來獲取這個JButton有哪些事件, 給使用者展示一個 事件清單 ,例如click , keyboardPressed 使用者可以選取一個, 然後就可以寫程式對這個事件程式設計了。”

 

“可是那個序列化有什麼用呢?”

 

“這是因為使用者設計完了以後,可能關掉Visual Java Builder 啊 , 如果不透過序列化把設計好的JButton儲存起來, 下次再開啟Visual Java Builder , 可就什麼都沒有了”

 

我想了想, 小碼哥設計的不錯,僅僅用了一個簡單的規範就滿足了視覺化編輯器的所有要求。 

 

"那我們就釋出這個規範吧, 我們們自己先做一個視覺化編輯器,給別人做個榜樣, 名稱我都想好了, 叫 NetBean 吧。"

 

劉欣(微信公眾號:碼農翻身)注:這是我杜撰的, 實際上NetBean這個名稱可能和java bean 並沒有實際關聯。 

 

果然不出我們所料, Java bean 釋出以後, 有力的帶動了Java 的IDE市場,  開發Delphi的Borland公司 也來插了一腳,搞出了一個JBuilder, 風靡一時。 

 

IBM 搞了一個Visual Age for Java ,  後來搖身一變, 成了一個叫Eclipse的開放平臺,超級受大家歡迎, 它反過頭來把我們的Netbean 和 JBuilder 逼的快沒有活路了。 

 

雖然我們玩的很歡,但是程式設計師根本不買賬, Java 在桌面開發市場還是沒有起色,使用Java bean 建立桌面程式的程式設計師少之又少, 只有部分像金融、ERP這樣的領地還在堅持。   

 

看來是無藥可救了。 

 

但是Java bean 何去何從 ? 丟棄掉太可惜了,   我和小碼哥商量了一下, 我們覺得:既然我們Java在統治了伺服器端的程式設計, 還是在那裡想想辦法吧......

提到Java bean 的規範雖然定義的不錯, 但卻沒有獲得意料中的成功, 尤其是Java帝國所期待的桌面開發元件化市場上。 

 

我和小碼哥多麼期待CSDN也能出一期《程式設計師大本營》, 裡邊包含成千上萬的java bean 元件啊。

 

不要幻想了, 趕緊把java bean 應用在伺服器端才是正事。

JSP + Java Bean

小碼哥建議先用在jsp上試試,  可以用java bean 來封裝業務邏輯,儲存資料到資料庫, 像這樣:

(微信公眾號"碼農翻身"注: 這其實叫做JSP Model 1 )

 

其中jsp 直接用來接受使用者的請求, 然後透過java bean 來處理業務, 具體的使用方法是:

 

這就能把HTTP request中的所有引數都設定到 user 這個java bean 對應的屬性上去。 

 

如果想偷懶, 還可以這樣:

 

 

當然要保證 http request中的引數名和 java bean 中的屬性名是一樣的。 

 

這個叫做JSP Model 1 的模型受到了很多Java程式設計師的歡迎 ,  因為他們的應用規模都很小, 用Model 1 使得開發很快速。

 

實際上, 這種方式和微軟帝國的asp , 以及和開源的php 幾乎一樣。 

 

但很快就有人來找我抱怨了, 說他們的專案中使用了Model 1 導致整個系統的崩潰。 

 

他說: “你知道嗎? 我們的系統中有好幾千個jsp, 這些jsp互相呼叫(透過GET/POST), 到了最後呼叫關係無人能搞懂。 ”

 

其實我們已經預料到了, 小碼哥對此有個形象的比喻:義大利麵條

這幾千個JSP 就像這碗麵條一樣,攪在一起, 根本理不清楚。

 

為了解決這個問題,小碼哥又推出了 :JSP Model 2 ,    這是個模型真正的體現了Model-View-Controller的思想:

Servlet 充當Controller ,  jsp 充當 View 

Java bean 當然就是Model 了!

 

業務邏輯, 頁面顯示, 和處理過程做了很好的分離。 

 

基於這個模型的擴充套件和改進,  很多Web開發框架開始如雨後春筍一樣出現, 其中最著名的就是Struts, SpringMVC 了。

 

Java Web開發迅速的繁榮了。 

 

我們再一次體會到了開放的好處 !

Enterprise Java bean

但是好景不長, 自從Java帝國統治了所謂的“企業級應用”開發領地, 各種各樣的遊行和抗議層出不窮:

“我們要分散式”

“我們要安全”

“我們要事務”
“我們要高可用性”

“......”

帝國分析了一下, 其實這些程式設計師的訴求可以歸結為:

“我們只想關注我們的業務邏輯, 我們不想, 也不應該由我們來處理‘低階’的事務, 多執行緒,連線池,以及其他各種各種的‘低階’API, 此外Java帝國一定得提供叢集功能, 這樣我們的一臺機器當機以後,整個系統還能運轉。 ”

 

我們不能坐著不管, 企業級應用是我們的命根子。

  

小碼哥徹夜工作, 最終拿出了一個叫做J2EE的東西, 像Java bean 一樣, 這還是一個規範, 但是比Java bean 複雜的多, 其中有:

 

JDBC :  Java 資料庫連線, 沒有資料庫的支援怎麼能叫企業級應用?

 

JNDI  :  Java 命名和目錄介面, 透過一個名稱就可以定位到一個資料來源, 連jdbc連線都不用了

 

RMI :  遠端過程呼叫,  讓一個機器上的java 物件可以呼叫另外一個機器上的java 物件 , 你們不是要分散式嗎?

 

JMS  :   Java 訊息服務,  可以使用訊息佇列了, 這不是企業級應用非常需要的嗎?

 

JTA :  Java 事務管理, 支援分散式事務, 能在訪問、更新多個資料庫的時候,仍然保證事務, 還是分散式。

 

Java mail  : 收發郵件也是必不可少的啊。

 

劉欣(微信公眾號號:碼農翻身)注:  J2EE 後來改成了Java EE。

 

當然還有最最最重要的升級, 小碼哥把java bean 變成了  Enterprise Java bean  , 簡稱  EJB

 

小碼哥宣稱: 

使用了EJB, 你就可以把精力只放在業務上了, 那些煩人的事務管理, 安全管理,執行緒 統統交給容器(應用伺服器)來處理吧。 

 

我們還提供了額外的福利, 只要你的應用伺服器是由多個機器組成的叢集, EJB就可以無縫的執行在這個叢集上, 你完全不用考慮一個機器死掉了應用該怎麼辦。我們都幫你搞定了。 

 

使用Session Bean , 可以輕鬆的處理你的業務。

 

使用實體Bean (Entity bean ) , 你和資料庫打交道會變得極為輕鬆, 甚至sql 都不用寫了。

 

使用訊息驅動Bean(Message Driven bean ) , 你可以輕鬆的和一個訊息佇列連線, 處理訊息。

 

聽起來很美好是不是? 

 

企業級開發領地的程式設計師們歡呼雀躍, 坐上了J2EE這條船,似乎一下子高貴起來, 開始鄙視微軟的ASP, 開源的PHP, Python 等“不入流”的語言了 。

 

Weblogic , Websphere等符合J2EE規範的應用伺服器趁勢而上, 搖旗吶喊, 彷彿一個新的時代要來臨了, 當然他們在背後一直悶聲發大財。 

Sring

有句古話是對的, 捧的越高, 跌的越慘。 

 

很快,大部分的程式設計師就發現, 美好的前景並沒有實現, EJB中用起來極為繁瑣和笨重, 效能也不好, 為了獲得所謂的分散式,反而背上了沉重的枷鎖。 

 

實體Bean很快沒人用了, 就連簡單的無狀態Session bean 也被大家所詬病, 其中一條罪狀就是“程式碼的侵入性”。

 

也是, 小碼哥定義EJB的時候沒考慮那麼多,程式設計師在定義一個Session bean的時候,需要寫一大堆和業務完全沒有關係的類。 

 

還需要被迫實現一些根本不應該實現的介面及其方法: 

為了哪怕一點點業務邏輯, 我們都得寫這麼多無用的程式碼 ! 程式設計師們出離憤怒了!

 

他們發起了一場叫做POJO (Plain Old Java Object)的運動, 高唱這POJO之歌, 要求我們整改。 

 

他們希望這個樣子:

public class HelloworldBean{

    public String hello(){

        return "hello world"

    }

}

與此同時,他們還過分的要求保留事務了, 安全了這些必備的東西。 

 

程式設計師確實不好伺候,   但是我們已經被Weblogic, Websphere這些大佬們“綁架”, 想改動談何容易 !

 

2002年, 小碼哥看到了Rod Johnson寫的一本書《Expert one on one J2EE development withoutEJB》 , 趕緊跑來找我:

“老大, 壞了壞了, 你快看看這本書吧, 這個叫Rod的傢伙寫的這本書直擊我們的命門,這廝要搞without EJB”

(微信公眾號"碼農翻身"注: 雖然這本書翻譯的很差,但由於是spring的開山之作,還是值得讀一下, 最好中英文對照)

 

豈止是without EJB,  他還“偷偷的”推出了一個叫什麼Spring的框架, 已經迅速的流行開了。

 

Spring 框架順應了POJO的潮流, 提供了一個spring 的容器來管理這些POJO, 好玩的是也叫做bean 。

 

看來我們的java bean 走了一圈又回到了原點。 

 

對於一個Bean 來說,如果你依賴別的Bean , 只需要宣告即可, spring 容器負責把依賴的bean 給“注入進去“, 起初大家稱之為控制反轉(IoC)

 

後來 Martin flower 給這種方式起來個更好的名字,叫“依賴注入”。

 

如果一個Bean 需要一些像事務,日誌,安全這樣的通用的服務, 也是隻需要宣告即可, spring 容器在執行時能夠動態的“織入”這些服務, 這叫AOP。 

 

後來我和小碼哥商量, 我們EJB也學習Spring , 簡化開發和配置, 但是為時已晚, Spring 已經成為事實上的標準了!程式設計師已經被spring 拉走了!

 

不過令我們欣慰的是, spring和spring mvc極大的增加了我們對web開發領地的統治力, java 帝國更加強盛了。 

 

(全文完)

 

Spring 的本質系列(1) -- 依賴注入

轉自:   劉欣     2016-06-25

轉載 碼農翻身微信公眾號 2016-06-25 劉欣 《Spring 的本質系列(1) -- 依賴注入》

1. 物件的建立

       物件導向的程式語言是用類(Class)來對現實世界進行抽象, 在執行時這些類會生成物件(Object)。 
       當然,單獨的一個或幾個物件根本沒辦法完成複雜的業務, 實際的系統是由千千萬萬個物件組成的, 這些物件需要互相協作才能幹活,例如物件A呼叫物件B的方法,那必然會提出一個問題:物件A怎麼才能獲得物件B的引用呢?
       最簡單的辦法無非是: 當物件A需要使用物件B的時候, 把它給new 出來 ,這也是最常用的辦法, java 不就是這麼做的?例如:  Apple a = new Apple();
       後來業務變複雜了, 抽象出了一個水果(Fruit)的類, 建立物件會變成這個樣子:
       Fruit f1 = new Apple();
       Fruit f2 = new Banana();
       Fruit f3 = ......
      很自然的,類似下面的程式碼就會出現:

       這樣的程式碼如果散落在各處,維護起來將會痛苦不堪, 例如你新加一個水果的型別Orange, 那得找到系統中所有的這些建立Fruit的地方,進行修改, 這絕對是一場噩夢。
 解決辦法也很簡單, 前輩們早就總結好了:工廠模式 

       工廠模式,以及其他模式像抽象工廠, Builder模式提供的都是建立物件的方法。這背後體現的都是“封裝變化”的思想。這些模式只是一些最佳實踐而已: 起了一個名稱、描述一下解決的問題、使用的範圍和場景,碼農們在專案中還得自己去編碼實現他們。

2. 解除依賴

        我們再來看一個稍微複雜一點, 更加貼近實際專案的例子:
        一個訂單處理類,它會被定時呼叫:  查詢資料庫中訂單的處理情況, 必要時給下訂單的使用者發信。

        看起來也沒什麼難度, 需要注意的是很多類一起協作了, 尤其是OrderProcessor , 它依賴於OrderService 和 EmailService這兩個服務,它獲取依賴的方式就是透過單例方法。
        如果你想對這個process方法進行單元測試--這也是很多優秀的團隊要求的-- 麻煩就來了。 
        首先OrderService 確實會從真正的資料庫中取得Order資訊,你需要確保資料庫中有資料, 資料庫連線沒問題,實際上如果資料庫連線Container(例如Tomcat)管理的, 你沒有Tomcat很難建立資料庫連線。
       其次這個EmailService 真的會對外發郵件, 你可不想對真正的使用者發測試郵件,當然你可以修改資料庫,把郵件地址改成假的,但那樣很麻煩, 並且EmailService 會丟擲一堆錯誤來,很不爽。
       所有的這些障礙, 最終會導致脆弱的單元測試: 速度慢, 不可重複,需要手工干預,不能獨立執行。
       想克服這些障礙, 一個可行的辦法就是不在方法中直接呼叫OrderService和EmailService的getInstance()方法, 而是把他們透過setter方法傳進來。

       透過這種方式,你的單元測試就可以構造一個假的OrderService 和假的EmailService 了。
       例如OrderService 的冒牌貨可以是MockOrderService , 它可以返回你想要的任何Order 物件, 而不是從資料庫取。MockEmailService 也不會真的發郵件, 而是把程式碼中試圖發的郵件儲存下來, 測試程式可以檢查是否正確。
       你的測試程式碼可能是這樣的:

        當然, 有經驗的你馬上就會意識到: 需要把OrderService 和 EmailService 變成 介面或者抽象類, 這樣才可以把Mock物件傳進來。 
        這其實也遵循了物件導向程式設計的另外一個要求: 對介面程式設計, 而不是對實現程式設計。

3. Spring 依賴注入

        囉囉嗦嗦說了這麼多, 快要和Spring扯上關係了。
       上面的程式碼其實就是實現了一個依賴的注入,把兩個冒牌貨注入到業務類中(透過set方法), 這個注入的過程是在一個測試類中透過程式碼完成的。
       既然能把冒牌貨注入進去,  那毫無疑問,肯定也能把一個正經的類安插進去, 因為setter 方法接受的是介面,而不是具體類。

       用這種方式來處理物件之間的依賴, 會強迫你對介面程式設計, 好處顯而易見。 
       隨著系統複雜度的增長, 這樣的程式碼會越來越多, 最後也會變得難於維護。 
       能不能把各個類之間的依賴關係統一維護呢?
       能不能把系統做的更加靈活一點,用宣告的方式而不是用程式碼的方式來描述依賴關係呢?
       肯定可以, 在Java 世界裡,如果想描述各種邏輯關係, XML是不二之選:

        這個xml 挺容易理解的, 但是僅僅有它還不夠, 還缺一個解析器(假設叫做XmlAppContext)來解析,處理這個檔案,基本過程是:
         0. 解析xml, 獲取各種元素
         1. 透過Java反射把各個bean 的例項建立起來: com.coderising.OrderProcessor   , OrderServiceImpl, EmailServiceImpl. 
         2. 還是透過Java反射呼叫OrderProcessor的兩個方法:setOrderService(....)  和 setEmailService(...) 把orderService , emailService 例項 注入進去。
        應用程式使用起來就簡單了:
        XmlAppContext ctx = new XmlAppContext("c:\\bean.xml");
        OrderProcessor op = (OrderProcessor) ctx.getBean("order-processor");
        op.process();
        其實Spring的處理方式和上面說的非常類似, 當然Spring 處理了更多的細節,例如不僅僅是setter方法注入, 還可以建構函式注入,init 方法, destroy方法等等, 基本思想是一致的。

       既然物件的建立過程和裝配過程都是Spring做的, 那Spring 在這個過程中就可以玩很多把戲了, 比如對你的業務類做點位元組碼級別的增強, 搞點AOP什麼的, 這都不在話下了。 

4. IoC vs DI

        “不要給我們打電話,我們會打給你的(don‘t call us, we‘ll call you)”這是著名的好萊塢原則。
        在好萊塢,把簡歷遞交給演藝公司後就只有回家等待。由演藝公司對整個娛樂專案完全控制,演員只能被動式的接受公司的差使,在需要的環節中,完成自己的演出。
        這和軟體開發有一定的相似性, 演員們就像一個個Java Object, 最早的時候自己去建立自己所依賴的物件,   有了演藝公司(Spring容器)的介入,所有的依賴關係都是演藝公司搞定的, 於是控制就翻轉了 
        Inversion of Control, 簡稱IoC。 但是IoC這個詞不能讓人更加直觀和清晰的理解背後所代表的含義, 於是Martin Flower先生就創造了一個新詞 : 依賴注入 (Dependency Injection,簡稱DI),  是不是更加貼切一點?

Spring本質系列(2)-AOP

原創:   劉欣     2016-06-30

   據說有些詞彙非常熱門和神奇, 如果你經常把它掛在嘴邊,就能讓自己功力大漲, 可以輕鬆找到理想的高薪的工作,這些詞就包括上一篇文章(《Spring本質系列(1) --依賴注入》)中聊過的IoC 和 DI, 也包括今天要聊的AOP。
       AOP(Aspect Oriented Programming)就是面向切面的程式設計, 為什麼是面向切面, 而不是物件導向呢?

1. 問題來源

      我們在做系統設計的時候,一個非常重要的工作就是把一個大系統做分解, 按業務功能分解成一個個低耦合、高內聚的模組,就像這樣:

       但是分解以後就會發現有些很有趣的東西, 這些東西是通用的,或者是跨越多個模組的:
        日誌:  對特定的操作輸出日誌來記錄
        安全: 在執行操作之前進行操作檢查
        效能: 要統計每個方法的執行時間
        事務: 方法開始之前要開始事務, 結束後要提交或者回滾事務
       等等....
       這些可以稱為是非功能需求, 但他們是多個業務模組都需要的, 是跨越模組的, 把他們放到什麼地方呢?
       最簡單的辦法就是把這些通用模組的介面寫好, 讓程式設計師在實現業務模組的時候去呼叫就可以了,碼農嘛,辛苦一下也沒什麼。

      這樣做看起來沒問題, 只是會產生類似這樣的程式碼:

      這樣的程式碼也實現了功能,但是看起來非常的不爽, 那就是日誌,效能,事務 相關的程式碼幾乎要把真正的業務程式碼給淹沒了。
      不僅僅這一個類需要這麼幹, 其他類都得這麼幹, 重複程式碼會非常的多。
      有經驗的程式設計師還好, 新手忘記寫這樣的非業務程式碼簡直是必然的。

2. 設計模式:模板方法

      用設計模式在某些情況下可以部分解決上面的問題,例如著名的模板方法:

       在父類(BaseCommand)中已經把那些“亂七八糟“的非功能程式碼都寫好了, 只是留了一個口子(抽象方法doBusiness())讓子類去實現。
       子類變的清爽, 只需要關注業務邏輯就可以了。
       呼叫也很簡單,例如:
       BaseCommand  cmd = ...  獲得PlaceOrderCommand的例項...
       cmd.execute();
       但是這樣方式的巨大缺陷就是父類會定義一切: 要執行哪些非功能程式碼, 以什麼順序執行等等
      子類只能無條件接受,完全沒有反抗餘地。
       如果有個子類, 根本不需要事務, 但是它也沒有辦法把事務程式碼去掉。

3. 設計模式:裝飾者

      如果利用裝飾者模式, 針對上面的問題,可以帶來更大的靈活性:

       現在讓這個PlaceOrderCommand 能夠列印日誌,進行效能統計
       Command cmd = new LoggerDecorator(
              new PerformanceDecorator(
                  new PlaceOrderCommand()));
       cmd.execute();

       如果PaymentCommand 只需要列印日誌,裝飾一次就可以了:
       Command cmd = new LoggerDecorator(
              new PaymentCommand());
       cmd.execute();
       可以使用任意數量裝飾器,還可以以任意次序執行(嚴格意義上來說是不行的), 是不是很靈活? 

4. AOP

       如果仔細思考一下就會發現裝飾者模式的不爽之處:
       (1)  一個處理日誌/效能/事務 的類為什麼要實現 業務介面(Command)呢?
       (2) 如果別的業務模組,沒有實現Command介面,但是也想利用日誌/效能/事務等功能,該怎麼辦呢?

       最好把日誌/安全/事務這樣的程式碼和業務程式碼完全隔離開來,因為他們的關注點和業務程式碼的關注點完全不同 ,他們之間應該是正交的,他們之間的關係應該是這樣的:

       如果把這個業務功能看成一層層面包的話, 這些日誌/安全/事務 像不像一個個“切面”(Aspect) ?
       如果我們能讓這些“切面“能和業務獨立,  並且能夠非常靈活的“織入”到業務方法中, 那就實現了面向切面程式設計(AOP)!

5. 實現AOP

       現在我們來實現AOP吧, 首先我們得有一個所謂的“切面“類(Aspect), 這應該是一個普通的java 類,不用實現什麼“亂七八糟”的介面。以一個事務類為例:

       我們想達到的目的只這樣的: 對於com.coderising這個包中所有類的execute方法, 在方法呼叫之前,需要執行Transaction.beginTx()方法, 在呼叫之後, 需要執行Transaction.commitTx()方法。
       暫時停下腳步分析一下。
      “對於com.coderising這個包中所有類的execute方法” , 用一個時髦的詞來描述就是切入點(PointCut) , 它可以是一個方法或一組方法(可以透過萬用字元來支援,你懂的)
      ”在方法呼叫之前/之後 , 需要執行xxx“ , 用另外一個時髦的詞來描述就是通知(Advice)
       碼農翻身認為,PointCut,Advice 這些詞實在是不直觀, 其實Spring的作者們也是這麼想的 :  These terms are not Spring-specific… unfortunately, AOP terminology is not particularly intuitive; however, it would be even more confusing if Spring used its own terminology.
       當然,想描述這些規則, xml依然是不二之選:

       注意:現在Transaction這個類和業務類在原始碼層次上沒有一點關係,完全隔離了。隔離是一件好事情, 但是馬上給我們帶來了煩 。
       Java 是一門靜態的強型別語言, 程式碼一旦寫好, 編譯成java class 以後 ,可以在執行時透過反射(Reflection)來檢視類的資訊, 但是想對類進行修改非常困難。 
       而AOP要求的恰恰就是在不改變業務類的原始碼(其實大部分情況下你也拿不到)的情況下, 修改業務類的方法, 進行功能的增強,就像上面給所有的業務類增加事務支援。
      為了突破這個限制,大家可以說是費盡心機, 現在基本是有這麼幾種技術:
      (1) 在編譯的時候, 根據AOP的配置資訊,悄悄的把日誌,安全,事務等“切面”程式碼 和業務類編譯到一起去。
      (2) 在執行期,業務類載入以後, 透過Java動態代理技術為業務類生產一個代理類, 把“切面”程式碼放到代理類中,  Java 動態代理要求業務類需要實現介面才行。
      (3) 在執行期, 業務類載入以後, 動態的使用位元組碼構建一個業務類的子類,將“切面”邏輯加入到子類當中去, CGLIB就是這麼做的。
      Spring採用的就是(1) +(2) 的方式,限於篇幅,這裡不再展開各種技術了, 不管使用哪一種方式, 在執行時,真正幹活的“業務類”其實已經不是原來單純的業務類了, 它們被AOP了 !、


一位阿里 Java 工程師的技術小站。作者黃小斜,專注 Java 相關技術:SSM、SpringBoot、MySQL、分散式、中介軟體、叢集、Linux、網路、多執行緒,偶爾講點Docker、ELK,同時也分享技術乾貨和學習經驗,致力於Java全棧開發!(關注公眾號後回覆”Java“即可領取 Java基礎、進階、專案和架構師等免費學習資料,更有資料庫、分散式、微服務等熱門技術學習影片,內容豐富,兼顧原理和實踐,另外也將贈送作者原創的Java學習指南、Java程式設計師面試指南等乾貨資源)

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69906029/viewspace-2653962/,如需轉載,請註明出處,否則將追究法律責任。

相關文章