01 February 2006

過年期間有一篇有關 Spring 的戰文:Bob Lee: I don't get Spring原作者 Bob 細述他眾多不爽 Spring 的地方,內容很多都是似是而非的論調 (因為他沒有用過 Spring... orz)。想當然爾,Spring 的用戶、作者群都紛紛跳出來指正他哪裡搞錯了。雖然如此,他的文章也不是完全沒有可取之處,他對 Spring 的懷疑、不解,有些正是 Spring 使用者常碰到的。

他提到一點: xml 太多,並且建立 IoC 的手續太過複雜(註)。說真的,這種事情常發生,我自個兒遇上了好幾次,不過這不是 Spring 的問題,而是 developer 自己的問題。舉個例:同樣是 jsp,有人就是會好好 follow MVC practice,而且利用 JSTL/EL 來簡化。但就是有人會寫一大堆 scriptlet,到了最後變的無法維護。MVC 是大家公認、獲得十來年應證的 design pattern,所以我們知道怎麼處理 jsp 較好。那 Spring 呢?它的 IoC 功能很強、彈性十足,可以寫的很複雜,也可以寫的很簡單,有沒有什麼 best practice 可以 follow?可惜的是 Spring 太年輕了,目前為止似乎沒有什麼準則教大家怎麼樣運用 IoC...

Best practice 是沒有,不過個人到是有一些的經驗和想法可以讓大家參考參考:

  • 先不要使用 Spring IoC
  • 當你在設計、撰寫 class 時,先不要依賴 Spring 的 IoC 功能,先將 wire 直接寫死在程式內。

    public class ReportManager {
        private SheetBuilder sheetBuilder ;
        public void setSheetBuilder(SheetBuilder sheetBuilder) {//...} 
        public void doReport() {...}
    }
    

    上面這個 ReportManager 需要 inject 一個 SheetBuilder 來協助他完成報表,如果是用 Spring,那就需要寫 xml 設定檔。我的建議是先寫死:

    public class ReportManager {
        private SheetBuilder sheetBuilder = new DefaultSheetBuilder();
        public void setSheetBuilder(SheetBuilder sheetBuilder) {//...} 
    }
    

    上面這個做法是直接在 construct 時便先產生一個 default 的 SheetBuilder 給 ReportManager。setter 仍然保留,因為測試的時候需要利用 setter 將 DefaultSheetBuilder 換成 Mock。用上面這個做法就不需要動用 Spring 了,所以也沒有 xml 複雜的問題。

  • 當元件出現重用時,才開始考慮使用 Spring IoC
  • 以上面的例子來看,SheetBuilder 如果一直都沒有人重用到,那就不必費神去動他了。如果寫到後面的 use case 發現另一個元件,比方說 AuditService 也要用到類似的Builder,那麼開始可以考慮 refactor SheetBuilder,並且在 Spring 裡面設定一個 sheetBuilder bean 讓兩個元件共用。當然也不一定兩個 use case 就要去運用Spring,也許三個、四個地方重用到了再考慮不遲。

  • 元件動用到外部資源時,使用 Spring IoC
  • 這一點其實有點像廢話... 因為這本來就是 Spring 的賣點 -_-。像是 DAO, Mail 這類的東西需要依賴外部的資料庫、SMTP server等等,這些 Spring 本身就提供了一些工具,當然要大用特用。如果你自己要整合 third party 的外部資源,比如上面的 SheetBuilder 其實是用買來的 ExcelBuilder 做成的,這個 ExcelBuilder 也許還要讀 xxx.properties 設定檔,還要....等等才能開始的運作。這些需要鎖碎設定的東西交給 Spring 再適合不過了。

  • 當元件需要 AOP 時,再考慮設定到 Spring xml
  • 比方說你想要替元件加上 log,或是想要做一些 Security 上的防護... 等等 AOP 的功能。這時再花工夫設定到 Spring 裡。這個講起來也有點像廢話啦.. (汗),不過重點在於該元件到底需不需要 AOP?上面的例子來看 SheetBuilder 如果要也加 Security 防護是不是太小題大做了點?ReportManager 加上 Security 還像樣點。當然,實際的狀況還是要看 use case 的特性來判斷。一般都希望能以最低的複雜度來滿足需求,千萬不要為了 AOP 而 AOP,不分大小、需不需要,通通硬上。

總結 -- 能不用 Spring IoC 就不用!

寫到 Spring 裡面的 bean 盡量是 Coarse-grained -- 也就是越上層、涵蓋的越廣越好!不要將一些鎖碎的小元件通通都倒到 Spring裡,這只會增加自己的痛苦而已,而且也享受不到 Spring 的好處。一般的專案大概就是 fooService (最上層的 bean) 和 fooDAO (需要外部資源) 這兩類的元件會使用 Spring IoC 來 wire。其他 business logic 所使用的 Domain 物件,即使它也應用了 IoC pattern,我想大多數的情況直接寫死在程式裡就夠了。

xml 太多,IoC 手續太複雜是指設計很多很小的元件,然後寫了很多的 setter。若想要將這些零零碎碎的物件湊合起來 (wire),勢必要寫很多 xml 檔的設定。可想而知,這寫起來一定很煩,而且 refactoring 也難。


回響

可以用 Tag <I>、<B>,程式碼請用 <PRE>