作為一個 JAVA 開發 ,Spring 是我用的最多的框架。Spring 這個名字起的很好,一語雙關,在英文里 spring 是春天的意思,寓意程序員的春天,同時 spring 還有彈簧的意思,它確實是一個有彈性的框架,擴展性很強。
類型約束
提到 spring ,首先想到的就是 ioc 和 aop 了,這些動態技術都是魔法,能自動管理,能增強代碼。但是動態技術也帶來了新的問題,Java 本來是個靜態語言,有很強的類型約束,卻被動態技術打破了。
@Cacheable(value = "userCache", key = "#corpId + #id")
public User findUserByCorpIdAndId(String corpId,String id){}
比如上面的代碼,我們可以使用 SpEL(Spring Expression Language)寫一些簡單的邏輯來構建 key,但是 SpEL 是沒有類型約束的,編譯沒有檢查,一般的 IDE 也沒有提示(Idea旗艦版有做支持)。Spring 框架在很多地方都是支持 SpEL 的,但是總是手寫這些字符串,很危險。我就犯過錯誤,寫錯了變量名但是程序仍然可以運行,只是參數拼上去總是 null,導致不同的參數拿到的緩存數據總是相同的。使用 api 的形式可以避免這樣的問題,但是便利性就下降了。
復雜度太高
Spring 可以說是很好的踐行了面向接口和面向對象編程,以致于繼承和依賴關系復雜到讓人嘆為觀止。對于 Spring 的源代碼我是不建議去讀的,因為復雜看很久沒有頭緒,方法跳了很久也沒有到真正的實現,由于大量應用了動態字節碼技術,有很多生成代碼的代碼,極為繁瑣。原理了解下即可,花太多時間去讀源碼,收益不大。不過對于一個框架來說,要解決復雜問題,自身也必然復雜。但是如果一旦遇到報錯要排查,或者調試代碼就很頭大了。
隨著生態的發展,Spring 已經是一個非常龐大的框架,學習曲線比較陡峭,需要一定的學習成本才能熟練使用,要掌握它是非常困難的。也因此發展出了 Spring 八股文,大量的相關面試題攻略出現在互聯網上。
資源占用高性能差
Spring 是一個 Bean 容器,可以讓我們避免過多的創建對象,由 Spring 來管理,還可以自動注入我們需要的對象實例。但是,Bean 的管理也是需要成本的,在啟動階段就要掃描所有的包或配置文件來讀取配置的 Bean,完成 Bean 的實例化再進行依賴注入和初始化,這就導致了規模一大啟動時間就會很長,而且一開始就占用大量的內存空間來存儲實例。而框架內部大量使用反射,也會降低運行時的性能。
根據我個人的經驗,一個中等規模的 Spring Boot 項目至少需要 1G 的內存。部署一個 Spring Boot 實例耗費的內存至少可以部署五六個 vert.x 實例。我負責的部分項目啟動時間最長的已經來到了1分半。
不可靠性
Spring 使用了大量的動態技術,動態技術來帶來了很多坑,這些坑甚至還成為了經典面試題,比如“spring事務失效的12種場景”。有坑是常見的,大部分時候解決問題的方案也會帶來新的問題。但是 spring 的坑有點多,不光是事務,凡是申明式注解,都會有不生效的問題,有時候不生效很難察覺,測試也不容易測出來,比如緩存和校驗,沒有生效結果也很可能是對的。也因此,有些公司的規范里是禁止使用申明式事務的。你可能會說經驗多了就好了,多注意點,老虎也有打盹的時候,何況開發人員工作強度那么大,心中難免會不踏實。
不可否認的是動態技術帶來了便利,假如我們想加代碼,可以不用修改原來的邏輯,單獨寫 aop 攔截邏輯就可以了。但是,這樣也帶來了很多不確定性,一段方法執行了哪些邏輯,我們可能并不知道。aop 技術還是盡量要少用的,寫的多了自己也忘記加過什么東西,將來接手項目的人可能要叫苦了。Spring Boot 的自動配置還有可能帶來安全問題,依賴的 jar 包如果有惡意代碼,是可以通過自動配置直接在啟動階段執行的。
反思
Spring 并不完美,動態字節碼技術也許不是好的解決方案,ioc 和容器技術不是必須的,這套方案只是在 Java 中比較流行而已。動態技術提升了靜態語言的靈活性,同時又失去了靜態語言的嚴謹,編譯期很多錯誤都檢查不出來,只有跑起來了才能知道。
雖然 ioc 降低了耦合性,但是我覺得并不是必須的,現實是我們的代碼常常沒有多實現,不需要無縫替換,也沒有必要熱插撥。大項目為了保險起見能跑就行,也不敢切換實現,更高一層的抽象還會導致失去部分原生特性,過渡設計也會增加程序的復雜度,不要總是面向對象和面對接口。
總結
我不反對使用 Spring ,畢竟對于這么有影響力的框架,如果團隊達不成一致,用它可以減少爭議,大家都服氣。但是,我已經不想再用了,探索別的方案對于我來說更有吸引力。
沒有必要始終如一的使用一個框架,更沒有必要死磕一種語言,Java 確實有很多不便之處,這個我們要承認,但是動態字節碼和代理技術不是良藥。多了解下其它的語言,開闊下視野很有必要,有時候格局就這樣打開了。