深入介紹Spring框架及故障排除
前言:
曾幾何時,Spring框架提供了比J2EE更輕量級和更靈活的解決方案。即使在2013年左右,我也很高興詳細(xì)了解當(dāng)時的新款Spring 4。如今,7年后,當(dāng)我看到春天的時候,我感到一陣恐慌。注釋和@ComponentScan
已經(jīng)用更好的東西取代了XML,這需要一個可視化工具來理解您的系統(tǒng)。Spring變成了一頭不斷生長(和變化)的水螅。我接手并試圖理解他人編寫的Spring應(yīng)用程序,這讓我很痛苦。最后但并非最不重要的一點是,Clojure教會了我代碼可以/應(yīng)該是多么簡單。那么,我對Spring的主要問題是什么?
(為了明確起見,當(dāng)我說Spring時,我指的主要是Spring控制反轉(zhuǎn)(IoC)及其依賴注入子集。)
Spring的缺點
不可理解性
系統(tǒng)構(gòu)成的黑箱:
注釋和組件掃描對于讓應(yīng)用程序快速啟動和運行來說非常棒。但當(dāng)您試圖理解所述應(yīng)用程序時,它們是一場噩夢。由于@Configurations
和@bean
來自您的代碼庫、公司庫中的任何地方,并且可能來自±100MB的Spring依賴項(真實情況),因此不可能清楚地了解應(yīng)用程序的結(jié)構(gòu)和配置。您基本上需要一次又一次地為Spring和所有庫進行RTFM,以便記住可能需要的所有內(nèi)容,并通讀整個代碼庫。諸如IntelliJ SpringBeans視圖之類的工具可能會有所幫助(如果您能夠正常工作的話)。
在這里,我完全同意Python的禪宗“顯式優(yōu)于隱式”
在一個典型的Clojure項目中,我轉(zhuǎn)到核心/主名稱空間,其中主函數(shù)啟動服務(wù)器并為其提供一個處理函數(shù),可能(手動)使用一些中間件包裝,可能在內(nèi)部使用庫進行路由。我還可能閱讀配置并傳遞它。我可以輕松地單擊瀏覽代碼,并查看代碼中的具體部分以及它們是如何協(xié)同工作的。即使是在cljdoc這樣的大型系統(tǒng)中。org有一個主功能啟動服務(wù)器(這里是Integrant),并為其提供配置和“系統(tǒng)定義”(類似于Springbean樹)。一切都是明確的和可導(dǎo)航的。
按注釋編程
能夠向方法和類添加元數(shù)據(jù)非常棒。我對@GetMapping(“/”)
之類的東西沒有任何反對意見。但它通常被用來繞過Java的限制,并通過諸如@Scheduled
和@Transactional
之類的注釋實現(xiàn)橫切關(guān)注點。我曾經(jīng)是AOP的堅定支持者,由于語言的限制,AOP仍然是Java開發(fā)人員不可或缺的工具,但我也意識到它的成本不容忽視。問題是,您無法輕松看到它在做什么(因為它什么都沒做,只是數(shù)據(jù))。為了給您一個透視圖,替換@Transactional Person findPersonInDb(String personId) {..}
在Clojure中,我只需要用一個自定義宏來包裝它,例如:
(defn find-person-in-db [person-id] (transactional ...))
實質(zhì)性的區(qū)別是,我可以控制單擊導(dǎo)航到宏,并查看它在做什么,因此所有行為都在那里,供我檢查和理解。祝你在Spring找到答案!
故障排除
Spring是讓很多事情快速起步的好時機——直到某些事情失敗或不按預(yù)期工作為止。Spring是一個松散耦合的意大利面大球,在我痛苦的經(jīng)歷中,解決它是非常困難的。有很多文檔,但我經(jīng)常找不到我需要的答案。也許官方文件太膚淺,有時依賴于大量已有的知識。搜索互聯(lián)網(wǎng)有時提供了一個解決方案,有時至少提供了有用的指針,有時提供了誤導(dǎo)性/舊信息,有時什么都沒有。在進行故障排除時,您需要艱難地通過這個龐大復(fù)雜的類,這些類以某種方式協(xié)同工作(或應(yīng)該協(xié)同工作),以(似乎)神秘的方式受到類路徑上的jar和@Configurations
的影響,并希望您能夠偶然發(fā)現(xiàn)問題的原因。
例如,我花了相當(dāng)長的時間來理解Spring MVC應(yīng)用程序中的錯誤處理是如何工作的。我們有一個@ControllerAdvice(["myapp.endpoint.api"]) myapp.endpoint.api.advice.ErrorHandler調(diào)用的ErrorHandler
(如果您記得拋出正確的異常類型),@Component myapp.spring.ErrorPagesCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>將container.addErrorPages(new ErrorPage("/error"))錯誤發(fā)送到org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController,然后它將神奇地呈現(xiàn)我們的錯誤。類路徑上的html。我意識到我再也不明白(如果我真的明白的話)它到底是如何工作的了。但要以所需的方式顯示錯誤,無疑是一場斗爭。
另一個難點是理解為什么Spring返回404而不是預(yù)期的文件。我對它的請求處理有很多來之不易的知識,也許有一天會成為一篇博客文章。
了解@Scheduled jobs
實際上是如何調(diào)度的,并試圖找出為什么一個作業(yè)沒有按預(yù)期運行,以及增加線程池大小以使其不再被較慢的作業(yè)停止的神奇調(diào)用是什么,這需要幾天的時間和多次失敗的嘗試,我甚至放棄了一次或兩次。搜索互聯(lián)網(wǎng)提供了一些幫助,但肯定遠(yuǎn)遠(yuǎn)不夠。這是我的一般經(jīng)驗,與Spring有關(guān)。
在Spring升級后搜索突然出現(xiàn)的ClassNotFound
運行時錯誤,試圖找出哪個Spring JAR有這個類,哪個版本是正確的,或者如何更改配置以停止需要更改…? 不,謝謝你。
為什么是Spring IoC?
這是一個由兩部分組成的問題:為什么是Spring IoC?為什么是Spring?
許多開發(fā)人員都知道,Spring是一個成熟且流行的解決方案。它還為幾乎所有問題提供了解決方案,這些解決方案通常能夠很好地協(xié)同工作。您也可以快速開始。另一方面,你可能會爭辯說,這是夸大其詞,有遺留問題,而且——通過嘗試為每個人做每件事——沒有做得完美,更小、專注的解決方案可能更好(盡管你需要整合它們)。
為什么選擇依賴注入和IoC?您可以從中找到許多原因:使用依賴注入和IoC容器的好處是什么?。
其中一些是:
- 簡化-您的類不需要知道如何創(chuàng)建它們的依賴項(以及它們需要什么)。您可以將實例化和連接類的問題分離出來。
- 靈活性—您現(xiàn)在可以提供不同的/修飾的實現(xiàn)。因此,在測試中,您可以提供模擬實現(xiàn),在大型復(fù)雜系統(tǒng)中,通過交換新的實現(xiàn)(這部分取決于對接口的編程),您可以使用疑難解答裝飾器包裝依賴項,從而使其更容易逐步重構(gòu)。
- 生命周期控制
查看維基百科上列出的優(yōu)點和缺點。但它有明顯的好處,這并不意味著你應(yīng)該在任何地方、任何事情上都使用它。記住成本和劣勢,做出有根據(jù)的決策。
我們通常在Clojure-f.ex中使用依賴注入。接下來是數(shù)據(jù)庫訪問庫。jdbc要求您將目標(biāo)數(shù)據(jù)源傳遞給每個調(diào)用。(這使您可以自由地創(chuàng)建自己的包裝器來控制數(shù)據(jù)源并將其傳遞給庫(如果您愿意的話)在Cognitect AWS API中構(gòu)造AWS客戶端時,可以讓它創(chuàng)建默認(rèn)的底層HTTP客戶端,也可以提供自己的客戶端,這樣就可以覆蓋默認(rèn)的依賴關(guān)系。我們甚至有一些人喜歡使用的依賴注入框架,如組件和上下文,而其他經(jīng)驗豐富的開發(fā)人員則覺得它們不必要。
選擇
當(dāng)談到Spring生態(tài)系統(tǒng)作為一個整體時,您應(yīng)該始終應(yīng)用Alex的合理庫原則:不要添加庫,直到?jīng)]有庫的痛苦如此之大,您無法忍受沒有它的生活(并且在適當(dāng)探索了其他選擇之后)。
當(dāng)您需要依賴注入時,最好手動組合系統(tǒng)。當(dāng)前的小型微服務(wù)時代與誕生Spring的巨型應(yīng)用時代截然不同。你可以自己做(為什么不?!)或者使用一種重量輕、重點突出的解決方案來解決更為手動的問題,例如Feather。盡可能喜歡編程配置。Feather仍然是由CDI的@Provides
驅(qū)動的注釋,但至少您在一個“module”類中聲明了這些注釋,您顯式地向Feather注冊了這些注釋,并顯式地向Feather請求所需的實例。在過去,我們使用Guice和手動調(diào)用來綁定每個微服務(wù)的主類。對于我這個當(dāng)時經(jīng)驗豐富的Spring用戶來說,這似乎很奇怪,也很錯誤,但我開始理解并欣賞它。甚至還有用于Spring Boot的(實驗性)編程配置DSL JaFu(Kotlin,KoFu也有一個)。
據(jù)一些人說,Jakarta(Java EE的后代)是Spring生態(tài)系統(tǒng)的一個更干凈、更小、更好的替代品。您還可以針對特定需求搜索單個解決方案。
我從一位受人尊敬的同事那里聽到了Micronaut的好消息,它提供了低開銷的DI和AOP、REST客戶端/服務(wù)器、反應(yīng)式、斷路器等,但它仍然依賴(似乎)類路徑掃描進行配置,因此保留了我在Spring中的主要問題。還有反應(yīng)型Helidon SE,它具有“透明的”的開發(fā)經(jīng)驗;純java應(yīng)用程序開發(fā),無注釋,無依賴注入”與這兩個領(lǐng)域相同的領(lǐng)域是Quarkus,但其IoC基于CDI,因此與Spring有相同的問題。Eclipse Vert.X 專注于反應(yīng)式、事件驅(qū)動的應(yīng)用程序有所不同,但提供了類似的功能(HTTP客戶端/服務(wù)器、OpenAPI、GraphQL、DB訪問、配置、斷路器、安全性、度量),并具有編程配置;相反,它不提供依賴注入(但您可能不需要它(盡管總線本身也有問題))。
其他人在說什么
在為本文做研究時,我發(fā)現(xiàn)了一些值得分享的經(jīng)驗和觀點。
著名的挪威軟件架構(gòu)師Johannes Brodwall寫道(2013年,再次遙遙領(lǐng)先于我):
我發(fā)現(xiàn)DI容器給我的一些直覺讓我改進了設(shè)計,但同時,我發(fā)現(xiàn)當(dāng)我移除容器時,解決方案變小了(這很好?。?,更易于導(dǎo)航和理解,更易于測試。我發(fā)現(xiàn)使用容器的成本非常高,這會導(dǎo)致復(fù)雜性和大小的增加,以及一致性的降低。
在這場Quora討論中,有很多好的建議:為什么大多數(shù)母語不是Java的程序員似乎對Spring框架持反對意見,他們對Spring框架的哲學(xué)有什么不喜歡的?
這方面的問題是:Spring有點破壞了我們從使用Java中獲得的簡單性好處。Spring謹(jǐn)慎地將復(fù)雜性引入到您的項目中,當(dāng)它工作時,框架表面上很簡單,但老實說,有多少人可以解釋Spring中發(fā)生的事情?調(diào)試Spring錯誤通??雌饋硐袷悄g(shù),需要90%的猜測和模式匹配。Spring通常可以在項目開始時為您節(jié)省數(shù)周的工作時間,您可能會覺得這些好處是免費的,但事實并非如此。在某些方面,您可以將其與在項目中使用動態(tài)編程語言的早期好處進行類比。在項目的初始階段,它會大大加快您的速度,但復(fù)雜性和技術(shù)債務(wù)會在稍后的階段打擊您。
(當(dāng)然,他與動態(tài)編程語言的比較與我和其他人使用Clojure的經(jīng)驗相反。)
所以他們改成了基于注釋的配置,被迫學(xué)習(xí),但我還是不喜歡。它仍然不是真正的Java。許多事情都是靠魔法發(fā)生的——當(dāng)它們沒有發(fā)生時,它們都以同樣的方式失敗,什么都沒有發(fā)生。因此,您編輯并重新編譯,同樣不會發(fā)生任何事情。無法判斷您是否注釋了錯誤的內(nèi)容,或者您的注釋所說的內(nèi)容是否與您認(rèn)為的不同,或者您是否構(gòu)建了錯誤的測試。
如果有像樣的文檔,我還沒有看到。我最近研究了RequestParam,它是Spring MVC中普遍存在的一部分,以了解其語義。該頁面實際上是無用的。Javadoc是API通信的主要方式;即使有更好的文檔存在,這個頁面也不會告訴我太多。
關(guān)于現(xiàn)代Java應(yīng)用程序:
Main方法有一行起始Spring和許多類,每個類有5個注釋。它們是如何被實例化的,以什么順序,如果出現(xiàn)問題,如何調(diào)試這個過程
下面是我最討厭Spring的幾件事:
它確實會減慢應(yīng)用程序的啟動時間,在運行應(yīng)用程序之前,你不會知道你的應(yīng)用程序是否正常工作——在大型商業(yè)環(huán)境中,這意味著最多需要一個小時的構(gòu)建和部署時間…然后會得到大量的stacktrace,其中一半與內(nèi)部spring類有關(guān),一旦您超越了簡單的單例布線場景,Spring就會變得非常丑陋、非常脆弱和不可預(yù)測,這有助于您起步,但您走得越遠(yuǎn),維護的噩夢就越大。
結(jié)論
那么,Spring是邪惡的,應(yīng)該不惜任何代價避免嗎?不需要。它使人們能夠克服Java的局限性,并提供了許多庫來解決實際問題。但它也是巨大、復(fù)雜的,而且維護成本很高。對引入庫持懷疑態(tài)度,考慮多種解決方案,并為您的案例選擇最佳解決方案,而不僅僅是Spring解決方案。即使在使用DI和IoC(Spring或其他)時,也要努力實現(xiàn)最大的透明度,并且更喜歡編程配置而不是類路徑掃描。如果其他一切都失敗了,請編寫良好的(java)文檔,以便您的繼任者能夠理解您的系統(tǒng)。
到此這篇關(guān)于深入介紹Spring框架及故障排除的文章就介紹到這了,更多相關(guān) Spring框架 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot+redis+阿里云短信實現(xiàn)手機號登錄功能
這篇文章主要介紹了springboot+redis+阿里云短信實現(xiàn)手機號登錄功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2024-01-01Springboot整合quartz產(chǎn)生錯誤及解決方案
這篇文章主要介紹了Springboot整合quartz產(chǎn)生錯誤及解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06Mybatis foreach用法解析--對于list和array
這篇文章主要介紹了Mybatis foreach用法解析--對于list和array,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03Mybatis使用MySQL模糊查詢時輸入中文檢索不到結(jié)果怎么辦
這篇文章主要介紹了Mybatis使用MySQL模糊查詢時輸入中文檢索不到結(jié)果的解決辦法的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07