兄弟倆暢游Tomcat城市的SpringMVC科技園區



Tomcat城市



Tomcat這座城市的歷史相當悠久了,經歷過幾次大的變遷后,呈現出非常明顯的地域特征。

從城市往西走,過了城鄉結合部以后,可以說是滿目瘡痍、一片破敗,這就是Servlet地區,這座城市一開始就是從這個地方發展起來的。

哎,這都是很多年前的老黃歷了,只有一些老人才知道這些,現在的年輕人都不到這個地方來了,於是就荒蕪了,快成無人區了。

城市的中央是Struts地區,人們習慣稱它為老城區。矮矮的居民樓,窄窄的街道,三五成群的老舊工廠。

雖然現在沒落了,但是置身其中,你依然能夠感受到它曾經輝煌過的痕跡,那時也應該是燈紅酒綠、人聲鼎沸、好不熱鬧。

現在這里只剩下一些老年人了,年輕人覺得這里太陳舊了,都紛紛搬走了,偶爾能見到幾個,那是回來看望父母的。

從城市往東走,出了老城進入新區,高樓大廈、玻璃幕牆,大寬馬路、人流成河。紅燈綠燈、南來北往,車聲人聲、聲聲不息。

這里充滿了大量的年輕人,節奏感、時尚感、科技感,有夢想、有壓力、有希望。沒錯,這就是大名鼎鼎、聞名遐邇的SpringMVC地區。

技術的發展就像城市的變遷,有新區就有老城。所謂長江后浪推前浪,一浪更比一浪浪,真是夠浪,嗯,golang。

編程新說注:


第一代web應用Jsp+Servlet,現在基本沒人用了,成了無人區了。

第二代web應用Struts1.x、Struts2,曾經輝煌時很多人用,現在都是進入維護期的老項目了,就像老城區。

第三代web應用SpringMVC,現在如日中天,依然是主戰場,就如同城市的新區。


不過SpringMVC並非固若金湯,它的挑戰者已經出現,就是響應式web應用,它現在不僅要面臨外患,還有來自內憂的困擾。

請看之前寫過的這篇文章“爸爸又給Spring MVC生了個弟弟叫Spring WebFlux”,了解更多的“同室操戈”。


破舊的火車站



request奉主人之命,坐了“一夜”的火車,“長途跋涉”后來到了tomcat城市,按照約定,他的弟弟response會來這里接他。request剛下了車,他弟弟就迎了上來,沒想到他跑到站台上來接自己了。

request邊走邊四處打量着,這座車站雖然略顯破舊,但結構設計合理,層層疊疊、環環相扣,真是建築之美啊。

他突然意識到自己是第一次來這里,還不知道路怎么走,看到不遠處有一老者在掃地,打算前去問路。眼看就要到了,不料被四個人“截胡”了。

其中兩個人說他們要找一個叫MyServlet的人,老者說出門往西走就行了。另外兩個人說要找一個叫FilterDispatcher的人,老者說出門往前走就行了。

看着他們四人離去的背影,老者無奈地搖了搖頭,又自顧自地開始掃地。request上去詢問為何這般,老者解釋道,這四位可是稀客啊,現在像他們這樣的人已經很少了。幾乎都是去找DispatcherServlet的人。

request說道,我們就是要去找DispatcherServlet呀,老者說,出門跟着人流走,保證能找到。為了禮貌,request詢問了老者的姓名,老者說,他是Wrapper,在這里工作十幾年了。

request和response跟老者道謝后,就離開了。出門后,好不容易擠上了一輛公交,一路向東奔去。

編程新說注:


MyServlet一般是一個剛畢業的學生起的名字。

FilterDispatcher是Struts2的核心控制器。

DispatcherServlet是SpringMVC的核心控制器。

Wrapper是Tomcat內部的一種容器組件,負責Servlet的調用執行。



SpringMVC科技園區



“前方到站SpringMVC科技園,有下車的乘客,請攜帶好隨身物品,從后門下車”,兩兄弟好不容易擠到后門,下車了。

眼前的這個科技園四四方方,里面的高大建築布局合理。門前的寬大道路干凈筆直,向南北無限延伸。旁邊的小路綠樹成蔭、鮮花滿地。

這里的一切都極具現代化都市氣息,兄弟倆早已忘我。一陣急促的嘈雜聲響起,哦,原來是綠燈亮了,可以過馬路了,隨機又淹沒在人群中。

兩兄弟在園區門口被保安攔下,“恁倆是弄啥嘞?”,保安問道。兩兄弟一聽,咦,河南人,心里樂了。說道,“老鄉,俺是來找一個叫DispatcherServlet的人”。保安道,“那中,他一般都可忙啦,恁倆先去那邊樹蔭下涼快涼快吧”。

一會兒功夫,有一個中年微胖男人來到了門口,就是他了。兩兄弟表明來意后,request遞上了一張“介紹信”,上面似乎寫着:


DispatcherServlet看后,心里暗罵一句,這是哪個小兔崽子在寫着玩呢。不過人既然已經來了,那就按照程序走吧。

他就帶着兩兄弟來到了一個房間門口,說先進去檢查一下,看看有沒有“攜帶大件行李物品”。只見response准備進去,一把被他拉回來,說你不用去,只要你哥哥去就行了。

request來到門前,只見上面寫着checkMultipart,推門而入,有個叫MultipartResolver的工作人員,正准備對他搜查,一看Content-Type,嘟囔着說原來只是普通表單提交沒有附件,隨即放棄了對request的檢查,讓他直接出去了。

request一臉懵逼,他原以為來到這里后,會有人專門帶着他參觀,給他講解,端茶倒水啥的。誰知就像進了醫院體檢一樣,拿個“單子”亂跑。

正在郁悶着的request在走過一個叫getHandler的房間門口時,被叫停了,他知道又該進去被檢查了。一個叫RequestMappingHandlerMapping的家伙坐在電腦后面,request趕緊遞上自己的單子。

那個家伙瞄了一眼單子后,在輸入框里敲上“POST /users”關鍵字,點擊搜索按鈕,只見結果的第一條就是一個叫UserController的小伙子。並把這個小伙子的信息打印到一張紙上給了request。

request接過紙,邊往外走邊看,只見上面寫着:


request又是一臉懵逼,這都什么玩意兒呀。不過定睛一看,發現了熟悉的字眼兒。如UserController、registerUser、User。

request隱隱約約當中記得自己的主人寫過一些和他們相關的東西,好像是這樣的:


此時,request仿佛明白了,剛才那個家伙根據我的“單子”,使用電腦搜索,為我開了個“方子”。說UserController這個小伙子的registerUser方法“可以治我的病”,其中User是方法入參。

request正准備沾沾自喜,怎么腦門突然一陣疼痛,莫非是得意忘形受了詛咒,哦,不是,是撞到門上了。揉了揉腦袋,便出了門。

三人一行繼續往前走,request心里明白,現在這充其量叫作“做檢查”,后面非給我來一個“大的修里”不可。又在一個叫做getHandlerAdapter的房間門口停住了。

不過這次兩兄弟都在外面等着,是DispatcherServlet親自拿着給request開的“方子”進去了。不一會他就出來了,又帶出來一位叫RequestMappingHandlerAdapter的人,說這位是高級技工,由他來完成一部分核心工作。

這位高級技工帶着兩兄弟向自己的地盤走去,來到了一個寫着handle的門前,推開門一起進入。這是一個非常大的房間,里面有好多的工作人員和機器設備,兩兄弟明白,是時候了,重大的事情將在這里發生。

高級技工讓兩兄弟躺到工作台上,然后讓所有人員各就各位,接着就是“生死看淡,不服就干”,於是,一切井然有序地開始了。

一個叫ServletInvocableHandlerMethod的家伙是本次的主要操盤手,他依次點名了自己的隊友和檢查了要用的設備,一切正常,下面正式開始了。

操盤手拿到給request開的“方子”,發現需要調用registerUser方法,於是先通過反射拿到這個方法的參數,再經過一番解析后變成了MethodParameter類型啦,對,它就表示方法的參數。

操盤手讓他的隊友ParameterNameDiscoverer去查看下參數的名字是什么,隊友拿到參數,驚奇地發現上面有個@ModelAttribute("user")注解,於是從注解中讀到了user,它就是參數的名字了。

操盤手又讓他的隊友HandlerMethodArgumentResolver去想辦法把參數值搞定,隊友也發現了@ModelAttribute("user")注解,說明這個參數是個模型數據,而且不是簡單類型。於是先打開設備ModelAndViewContainer,發現設備里並沒有一個叫user的數據。

隊友明白,需要自己來生成這樣的一個參數了。先拿到參數類型User,然后反射一下構造函數,發現正好有個默認無參的,通過它就new出了一個User類型的對象了。

隊友接着反射一下它的屬性,發現有4個,username、password、email、age。接着從request中恰巧能找出這4個名稱的值,使用WebDataBinderFactory設備把數據類型合理轉化后,設置給了user對象。這樣隊友就把參數值給准備好了。

有了registerUser方法和user參數后,還要知道在哪個對象上調用才行啊,於是操盤手根據方法所在的類型UserController,去容器中找到它的bean實例,接着就在該實例上通過反射發起了方法調用,傳進去入參,並獲取返回結果。

操盤手拿到返回結果,簡單檢查后發現返回結果不為null,再檢查request的弟弟response,發現沒有出現錯誤,而且還沒有執行結束。於是在ModelAndViewContainer設備上把該請求標記為尚未處理完。

然后把返回結果交給隊友HandlerMethodReturnValueHandler去處理,隊友發現方法所在的類UserController上標有@ResponseBody注解(是作為@RestController的元注解出現的),瞬間就明白方法的返回值是直接作為web請求的響應的。

由於方法的返回值是要直接寫入response的,所以就完事了,不用考慮視圖解析這一塊了。因此隊友就在ModelAndViewContainer設備上把本次請求標記為已處理完成。

接着就把方法的返回值交給自己的好朋友HttpMessageConverter去處理,好朋友看了request的“單子”一眼,發現上面有Accept:application/json,瞬間也明白了,原來他想要的是JSON格式呀。

於是把方法返回值發給合作伙伴Jackson,不一會給他發回了結果,{"code":0,"desc":"success"},好朋友把這個結果甩給了response,叫他拿好了。

好朋友完成了隊友的任務,隊友完成了操盤手的任務,操盤手向高級技工報告,任務已成功完成,請檢閱。

高級技工本來打算輸出一個ModelAndView作為處理結果呢,一檢查ModelAndViewContainer設備發現請求已被處理完了。罷了,那就返回一個null吧。

門開了,兩兄弟出來了,哥哥request已被“消耗殆盡”,弟弟response“滿載而歸”。DispatcherServlet早已在此等候,他看到高級技工手里只有一個null,於是記錄了一句話,“No view rendering, null ModelAndView returned.”。

兩兄弟和DispatcherServlet道謝后來到了園區大門口,接着和老鄉保安揮手告別。此時天色已晚,擠上一輛公交車后,直奔火車站而去。



就此一別,再無相見



一路搖搖晃晃來到火車站,天已完全黑透了。返程的列車早已整裝待發,弟弟response拉着哥哥的手准備一起上車,被哥哥拒絕了,哥哥說按照劇情應該只有你一個人回去。我的使命已完成了。

弟弟並不明白哥哥是什么意思,就問道那我們還能不能再見面。哥哥笑着說傻孩子,“當然可以了”。弟弟高興地跳上了車。

伴着一聲長鳴,列車啟動,兄弟倆互相揮手告別,列車漸漸消失在黑夜的黑中。弟弟沒有看到哥哥微笑的眼角流下了流水。

只有哥哥心里明白,他和弟弟,就此一別,再無相見。轉身向車站外走去,看到那個老者依然在自顧自的掃着地。

黑白無常拿着腳鐐手銬,早已在此“恭候多時”,有氣無力的request全然無法反抗,任由這“二鬼”拖着去“陰曹地府”接受JVM的輪回。

也許老天不願意看到一個光榮完成使命的人就這般的“煙消玉損”,就派出了鍾馗來解救他。鍾馗打跑了黑白無常,希望帶request“永生”。

request婉言拒絕,說我非“三界五行”之外,我依然是凡人,依然有自己的宿命。這是任何人都無法逃離的自然規律。

頃刻,一束白光從天而降,灑滿request的全身,只見request張開雙臂,身輕如燕般的飛向光的源頭,不一會便沒有了蹤跡。

 

(END)


作者是工作超過10年的碼農,現在任架構師。喜歡研究技術,崇尚簡單快樂。追求以通俗易懂的語言解說技術,希望所有的讀者都能看懂並記住。下面是公眾號和知識星球的二維碼,歡迎關注!

 

       

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM