過年期間有一篇有關 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 複雜的問題。
以上面的例子來看,SheetBuilder 如果一直都沒有人重用到,那就不必費神去動他了。如果寫到後面的 use case 發現另一個元件,比方說 AuditService 也要用到類似的Builder,那麼開始可以考慮 refactor SheetBuilder,並且在 Spring 裡面設定一個 sheetBuilder bean 讓兩個元件共用。當然也不一定兩個 use case 就要去運用Spring,也許三個、四個地方重用到了再考慮不遲。
這一點其實有點像廢話... 因為這本來就是 Spring 的賣點 -_-。像是 DAO, Mail 這類的東西需要依賴外部的資料庫、SMTP server等等,這些 Spring 本身就提供了一些工具,當然要大用特用。如果你自己要整合 third party 的外部資源,比如上面的 SheetBuilder 其實是用買來的 ExcelBuilder 做成的,這個 ExcelBuilder 也許還要讀 xxx.properties 設定檔,還要....等等才能開始的運作。這些需要鎖碎設定的東西交給 Spring 再適合不過了。
比方說你想要替元件加上 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 也難。