天天看點

為什麼我們要使用ssh架構技術,及感想 前言: 原文如下:

      在公司從c++轉向java web方向大概有3個月(11月初-1月底)了。三個月前對java和web還幾乎是零基礎。然後從安裝eclipse,mysql,tomcat開始,到學習html/css/javascript,jquery,jsp,sql,在項目已有的架構spring-struts-hibernate上,開始加入新的功能頁等。這三個月,寫了很多的javascript代碼,修改資料庫設計,調試網頁的控件布局等。從對java web恐懼,到逐漸喜歡上了這種開發模式。

     java和web的内容似乎比c++要多很多很多需要學習的東西,還有各種各樣的架構知識。雖然現在接到一個需求,基本能夠清楚怎樣在原來的項目上進行修改,但是對底層架構的不了解讓我覺得自己目前是站在一個空中樓閣上面,因為我不知道自己什麼時候就會因為不清楚架構的那個原理或特性,而讓整個代碼奔潰而無法運作。

      之前在公司完成一個功能後,在本地的機器上運作完全正常。但是當rebase到項目的主分支後,tomcat都無法正常啟動。後來查了将近1個多小時,才找到問題是由于我和主分支有一個類的名稱不同。而我rebase時,在對spring的xml文檔解決沖突時,把bean中property的name修改成我的類名了,但是我不知道這裡的name其實就是相關聯的某個.java檔案中類的引用對象。而引起這個問題的原因我想最主要的就是我對spring機制的“依賴注入”不了解。

      以下這篇文字出自部落格園的一位web前端的資深人士的部落格,部落客名為“夏天的森林”,部落格題為《為什麼做java的web開發會使用struts2,springmvc和spring這樣的架構?》

      由于原文排版不好,導緻看起來比較費眼。是以我将原文适當多分了幾段,并且加了一些标題。

作者: 夏天的森林

标題: 為什麼做java的web開發會使用struts2,springmvc和spring這樣的架構?

---------------------------------------------------------------------------

      今年我一直在思考web開發裡的前後端分離的問題,到了現在也頗有點心得了。随着這個問題的深入,再加以現在公司很多web項目的控制層的技術架構由struts2遷移到springmvc,我突然有了一個新的疑問無法得到正确的解釋,為什麼我們現在做java的web開發,會選擇struts2或者springmvc這樣的架構,而不是使用servlet加jsp這樣的技術呢?特别是現在我們web的前端頁面都是使用velocity這樣的模闆語言進行開發,抛棄了jsp,這樣的選擇又會給我們java的web開發帶來什麼樣的好處,沿着這個問題的思路,我又發現新的疑問,為什麼現在很多java企業級開發都會去選擇spring架構,spring架構給我們開發的應用帶來了什麼?這麼一想還真是問倒我了,我似乎很難找到一串能讓人完全信服的答案,最終我發現,這些我認為我很熟悉很常用的技術,其實還有很多讓我陌生不解的地方,這些陌生和不解的地方也正是我是否能更高層次使用它們的關鍵。

      今天這篇文章我就來講講這些問題,不過struts2,spring這樣的技術經過這麼多年的積累已經是相當龐大和複雜,它們的面很廣,本人雖然已經用了它們多年,還是有很多技術不熟悉和不清楚,是以本文不是全面對我題目做出解答的文章,而是根據我現有知識結構來了解這個問題。

      軟體裡有很多優秀的架構。有一種類型的架構,它的特點是建立在一個現有技術的基礎上,提供和現有技術一樣業務功能的技術架構,這個新的技術架構比原技術更加易用,更加健壯同時功能更加強大,例如:jquery,以及本文所要談到的struts2和springmvc。深究這些架構都是相當之複雜,但是它們的優點其實隻有一個:就是讓使用者隻關心核心業務的開發,架構幫你屏蔽原有技術跟業務開發無關的各類技術問題。像jquery,struts2或springmvc這類架構之是以優秀,就是它們在這點上做的太好了,以至于很多使用它的程式員都已經不清楚原有技術的真實面目,是以我們要将struts2了解得更好,使用的更加熟練和深入,這裡我們就要跳出struts2的技術,到struts2技術的源頭servlet,仔細研究下servlet的特點,隻有這樣我們才能把struts2架構學得更好。

      servlet的作用是接收浏覽器傳給服務端的請求(request),并将服務端處理完的響應(response)傳回給使用者的浏覽器。

      浏覽器和服務端之間通過http協定進行溝通,其過程是:浏覽器根據使用者的選擇将相關資訊按http協定封包的規範組裝請求的http封包,封包通過網絡傳輸到指定的伺服器,伺服器通過特定的web容器接收這個封包資訊,例如:tomcat,jetty,jboss這樣的web容器。web容器會将http封包解析出來,如果是使用者請求,最終解析出來的封包資訊會用一個request對象存儲起來,服務端使用這個request做完相應的處理後,服務端程式将結果資訊封裝到response對象裡,然後将response對象交給web容器,web容器則把這個response對象轉變為http協定的封包,并将封包回傳給浏覽器,浏覽器最後解析這個響應封包,将最終結果展示給使用者。

為什麼我們要使用ssh架構技術,及感想 前言: 原文如下:

      web容器創造了servlet接口,servlet接口就是開發人員自己實作業務邏輯的地方。程式員開發servlet就好比做填空題,而填空題的語境或者說上下文提示就是由request和response對象,但是javaee規範裡的servlet接口很簡單,就三個方法init,service和destory。但是這個接口太籠統,是以規範裡還提供了一個httpservlet類,這個類根據http請求類型提供了doget,dopost等方法,servlet接口最大的特點就是根據http協定的特點進行定義,是以做servlet開發時,如果使用者對http協定特點不是特别熟悉,都會碰到或多或少令人迷惑的問題,特别是碰到一些複雜特殊的請求時候:例如檔案上傳,傳回特殊的檔案格式到浏覽器,這時候使用servlet開發就不是很友善了。servlet開發還有個問題可能大家常常被忽視,就是請求資料的類型轉化,http協定傳輸都是文本形式,到了web容器解析後也是文本類型,如果碰到貨币,數字,日期這樣的,類型需要我們根據實際情況進行轉化。如果頁面傳送的資訊非常多,我們就不得不做大量類型轉化,這種工作沒有什麼技術含量,是個體力活而且很容易導緻程式錯誤。同時java的企業開發都是圍繞javabean進行,類型轉化好的資料還要封裝到對應的javabean裡,這種轉來轉去的事情對于項目開發絕對不是什麼好事情。

       是以古老的struts1為這種問題找到了一種解決方案,就是定義了一個dto對象(資料傳輸對象),專門負責做這樣的事情。不過到了struts2,整個替代servlet的action本身就是一個javabean。

       java的企業開發一個技術特點就是使用javabean進行的,struts2的特點之一就是它替代servlet的操作類,就是一個典型的javabean。

      首先struts2架構将頁面傳輸的資料進行類型轉化和封裝後,将請求資訊封裝到了這個javabean的屬性裡,這樣我們開發web程式時就省去了煩心的類型轉化和封裝的問題。

      前面我講到傳統的servlet是根據http協定進行定義的,它會按你請求方式(post還是get方式)來處理使用者的請求。但是對于一名程式開發人員而言,一個請求,具體到一個url,其實對于服務端而言就是服務端對外提供的一個功能,或者說是服務端對外的一個動作。如果我們使用servlet開發程式我們就得把http的動作轉化為具體的業務動作,這就讓程式開發變得繁瑣,增強了開發的難度。是以struts2替代servlet的javabean就屏蔽了servlet裡http的請求方式和具體業務動作轉化的問題。javabean裡的每一個方法都可以和每一個url請求一一對應,這必然減輕了開發的難度問題。

      servlet另一個作用就是構造response對象,讓頁面獲得正确的響應。其實作代的浏覽器是一個多媒體工具,文字、圖檔、視訊等等東西都可以在浏覽器裡顯示,資源的不同就會導緻http響應封包的差别。如果我們使用servlet開發就要根據資源的不同在java程式裡用寫死的形式處理,這樣的程式很難複用,而且如果程式員對某種資源的處理了解不到位,就會導緻問題的出現。struts2通過配置檔案的形式将這樣的邏輯從java程式裡剝離出來,使用配置的方式進行統一管理。這個做法和spring的aop方式類似。這樣就讓結果處理方式更加統一,更加利于管理,同時也提升了程式的健壯性以及降低了開發的難度。

      servlet在mvc開發模式裡就是其中c層即控制層。控制層就像俄羅斯的雙頭鷹(一個頭向東看一個頭向西看)一樣,一個頭向m層模型層看,一個頭向v層視圖層看。模型層也是用java編寫的,控制層也屬于服務端語言開發,是以m層和c層的溝通沒有天然的障礙,但是和v層視圖層就不一樣了。這是一個跨語言的溝通,對于浏覽器,它隻懂得html,javascript和css,浏覽器是了解不了java這種語言的東西,但是要讓服務端的東西能被浏覽器了解接受,我們就必須得把服務端的響應資訊放到頁面裡,是以就需要一個技術把java的資訊轉化到html頁面裡,這就是javaee規範裡提供了jsp技術。

      jsp其實是一種服務端技術而非用戶端技術,不過它看起來似乎更像html技術。最早的jsp開發裡都是直接将java代碼寫到頁面裡,這種壞處誰都知道,之後javaee規範提供了自定義标簽技術,使用一種類似html标簽的方式來解析java代碼。

      struts2架構提供了一整套完整的自定義标簽技術,這似乎聽起來不算啥,但是它的作用非凡,因為自定義标簽之是以叫自定義就是每個人都可以自己來定義,如果沒有一個規範必然産生混亂,而且一套完善的自定義标簽是個系統工程,一套完整的自定義标簽相當于我們在自己定義一套新的開發語言。做程式的人聽到這個一定就會明白開發一套完整的自定義标簽的工作量和開發難度都是難以想象的,而且自定義标簽都是和控制層緊密相連,其難度又會增加一個次元,是以struts2提供的自定義标簽對于業務開發帶來的将是質的飛越。

      servlet裡還有兩個重要的技術:監聽器和過濾器。對于監聽器在web開發裡使用的場景比較少,都是一些十分特别的情況才會使用,大部分web開發裡可以忽略它的使用,我們用的最多的監聽器可能就是對servletcontext建立和銷毀的監聽器,servletcontext是整個web應用的全局對象,它和web應用的生命周期綁定在一起,是以使用這個監聽器對web應用的全局資訊進行初始化和銷毀操作,例如spring容器的初始化操作。

      比較有意思的是過濾器,在struts2裡有個攔截器,它們的作用相同都是用來攔截請求的,因為攔截器是struts2的特有功能,在struts2裡使用攔截器自然比使用過濾器更順手,其實攔截器所用的技術比過濾器更加先進,因為攔截器使用了反射技術,是以攔截器攔截的面更大,控制請求的能力更強,它能完成的任務也會更加的豐富多彩。

      在我第一次接觸struts2時,有人告訴我struts設計的一個目的就是想屏蔽在控制層裡操作request和response對象,因為這兩個http協定的兒子會造成web開發裡思路的混亂,但是我在實際開發裡卻經常不自覺地使用這兩個對象。而且本人做前端開發非常喜歡使用ajax,使用ajax技術時候我就很讨厭struts2的自定義标簽,我更加喜歡在頁面裡用javascript技術處理各種資訊,最終struts2在我眼裡就是一個servlet的變體,是以曾經有段時間我常常在想是不是可以抛棄struts2,直接用servlet。因為struts2裡用到了太多反射機制,特别是使用注解做配置(注解是用反射實作的)。在java裡反射的執行效率是非常低的,直接使用servlet一定能提升web應用的執行效率。其實這個倒很難做到,因為當時我沒法在servlet裡靈活地運用spring技術。

      spring技術可以說是java企業開發裡最重要的技術,不過真的了解spring的作用和意義還真是一件麻煩的事情,很多人對spring了解其實都是停留在使用階段(例如:聲明式事務很好用等等),當今的spring技術生态環境裡可謂是蔚為壯觀,spring已經包羅萬象,它的内容之多完全不亞于它的本源java語言了,而spring這麼大的架構都是建立在ioc和aop技術之上,隻有深入了解了這兩個技術我們才能明白為什麼spring這個框能裝的下那麼多東西了。

      首先是ioc,ioc技術第一個解釋叫做控制反轉,它還有個解釋就是依賴注入。這兩個名字很難從字面了解,但是當你了解它的原理後就會發現它們的描述是何等準确。

      ioc技術的本質就是建構對象的技術,換句話說就是将一個類執行個體化成對象的技術。在java裡執行個體化類通過new關鍵字進行的,每次new一個類都會産生一個新的執行個體對象,這麼做似乎很浪費,有時這種浪費還挺危險。因為在程式開發時候,我們常常隻需要某個類永遠隻能産生一個執行個體對象,這個時候就得使用單例模式,此外在設計模式裡還可以通過工廠方式産生對象。使用過spring的人看到上面的文字就知道了,spring裡bean的定義就和上面的内容一一對應,scope屬性single産生單例對象,prototype産生新對象,bean還可以通過工廠方式産生對象,可以說spring的bean就是制造對象的工具。

      面向對象程式設計裡,對象相當于現實生活中的一個實體。例如我們有個對象,作用是完成打獵的操作。那麼打獵這個對象,内部包含兩個輔助對象:人和槍。隻有将人和槍賦予了打獵這個對象,那麼“打獵對象”才能完成打獵的操作,但是建構一個人和槍的對象并不是看起來那麼簡單。這裡以槍為例,要創造一把槍我們需要金屬、需要機床和子彈,而機床和子彈又是兩個新對象,這些對象一個個互相嵌套互相關聯。大夥試想下如果我們在java代碼裡建構一個槍的對象那是何其的複雜,假如我們要構造的不是簡單的槍對象,而是更加複雜的航空母艦,那麼構造這個對象的成本之高是讓人難以想象的。

      怎麼來消除這種對象互相嵌套互相依賴的關系呢?spring提供了一種方式,這種方式就是spring提供一個容器,我們在xml檔案裡定義各個對象的依賴關系,由容器完成對象的建構。當我們java代碼裡需要使用某個執行個體的時候就可以從容器裡擷取,那麼對象的建構操作就被spring容器接管,是以它被稱為控制反轉。控制反轉的意思就是本來屬于java程式裡建構對象的功能交由容器接管;依賴注入就是當程式要使用某個對象時候,容器會把它注入到程式裡,這就叫做依賴注入。

      在java開發裡我們想使用某個類提供的功能,有兩種方式,一種就是構造一個新的類,新的類繼承該類;另一種方式則是将某個類定義在新類裡,那麼兩個類之間就建立一種關聯關系。spring的ioc容器就是實作了這種關聯關系(記住不是繼承關系哦)。那麼某個類要被賦予到新類有哪些辦法呢?一般隻有兩種:一種就是通過構造函數,一種就是通過setxxx方式,這也是spring容器使用到了兩種标準的注入方式。

      不管是繼承方式還是關聯方式,其實都是增強目标對象能力的開發手段。在設計模式裡有一種代理模式,代理模式将繼承模式和關聯模式結合在一起使用,代理模式就是繼承模式和關聯模式的綜合體,不過這個綜合體的作用倒不是解決對象注入的問題,而是為具體操作對象找到一個保姆或者是秘書,這就和小說裡的二号首長一樣,這個二号首長對外代表了具體的執行個體對象,執行個體對象的入口和出口都是通過這個二号首長,因為具體的執行個體對象是一号首長,一号首長是要幹大事的,是以一些事務性,重複性的工作例如泡茶,安排車子,這樣的工作是不用勞煩一号首長的大駕,而是二号首長幫忙解決的,這就是aop的思想。aop解決程式開發裡事務性,和核心業務無關的問題,但這些問題對于業務場景的實作是很有必要的,在實際開發裡aop也是節省代碼的一種方式。

      spring的核心技術的作用本質就是一個溝通機制,spring總是盡全力的讓溝通的雙方資訊暢通,同時降低雙方的溝通成本,在現實機構裡一個善于溝通的人肯定是該公司的上司,很會溝通的上司能調動起各種資源的積極性,善于溝通的上司就會做到海納百川,讓各種不同人追随他,是以當今的spring就是一個大框,什麼都可以往裡裝。

      spring很像銀行,它不能直接創造物質财富,但是一切資源都要通過它進行流通,它能控制經濟發展的走向,回到程式的世界。spring的作用是被标榜為程式之間的解耦,spring能降低不同子產品之間的耦合度,原因就是在程式開發裡不同子產品之間資訊的溝通是通過對象傳遞完成的,而對象能否順利傳遞就是要合理地建構好對象,而管理好對象的建構方式就能管理好對象傳遞,這就是spring給系統架構設計帶來的好處。