目前不少公司的架構,從Struts2遷移到了SpringMVC。你有想過爲何不使用Servlet+JSP來構建Java web項目,而是採用SpringMVC呢?javascript
既然這樣,咱們從源頭提及。Struts2的源頭其實也是Servlet。Servlet的做用是接收瀏覽器傳給服務端的請求(request),並將服務端處理完的響應(response)返回給用戶的瀏覽器,瀏覽器和服務端之間經過http協議進行溝通,其過程是瀏覽器根據用戶的選擇將相關信息按http協議報文的規範組裝請求的http報文,報文經過網絡傳輸到指定的服務器,服務器經過特定的web容器接收這個報文信息,例如:tomcat,jetty,jboss這樣的web容器,web容器會將http報文解析出來,若是是用戶請求,最終解析出來的報文信息會用一個request對象存儲起來,服務端使用這個request作完相應的處理後,服務端程序將結果信息封裝到response對象裏,而後將response對象交給web容器,web容器則把這個response對象轉變爲http協議的報文,並將報文回傳給瀏覽器,瀏覽器最後解析這個響應報文,將最終結果展現給用戶。css
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。html
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方式相似,這樣就讓結果處理方式更加統一,更加利於管理,同時也提高了程序的健壯性以及下降了開發的難度。java
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裏使用攔截器天然比使用過濾器更順手,其實攔截器所用的技術比過濾器更加先進,由於攔截器使用了反射技術,所以攔截器攔截的面更大,控制請求的能力更強,它能完成的任務也會更加的豐富多彩。web
在我第一次接觸struts2時候,有人告訴我struts設計的一個目的就是想屏蔽在控制層裏操做request和response對象,由於這兩個http協議的兒子會形成web開發裏思路的混亂,可是我在實際開發裏卻常常不自覺的使用這兩個對象。並且本人作前端開發很是喜歡使用ajax,使用ajax技術時候我就很討厭struts2的自定義標籤,我更加喜歡在頁面裏用javascript技術處理各類信息,最終struts2在我眼裏就是一個servlet的變體,所以曾經有段時間我經常在想是否是能夠拋棄struts2,直接用servlet,由於struts2裏用到了太多反射機制,特別是使用註解作配置(註解是用反射實現的),在java裏反射的執行效率是很是低的,直接使用servlet必定能提高web應用的執行效率。其實這個倒很難作到,由於當時我無法在servlet裏靈活的運用spring技術。ajax
說完Servlet+jsp技術到Struts2技術的過渡。接下來談談Spring。spring
spring技術能夠說是java企業開發裏最重要的技術,不過真的理解spring的做用和意義還真是一件麻煩的事情,不少人對spring理解其實都是停留在使用階段(例如:聲明式事務很好用等等),當今的spring技術生態環境裏可謂是蔚爲壯觀,spring已經一應俱全,它的內容之多徹底不亞於它的本源java語言了,而spring這麼大的框都是創建在ioc和aop技術之上,只有深刻理解了這兩個技術咱們才能明白爲何spring這個框能裝的下那麼多東西了。sql
首先是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給系統架構設計帶來的好處。
說到Spring, Spring的事務你懂嗎?
什麼是事務?爲何事務要管理?什麼是Spring事務?事務就是對一系列的數據庫操做(好比插入多條數據)進行統一的提交或回滾操做,若是插入成功,那麼一塊兒成功,若是中間有一條出現異常,那麼回滾以前的全部操做。這樣能夠防止出現髒數據,防止數據庫數據出現問題。開發中爲了不這種狀況通常都會進行事務管理。在JDBC中,是經過Connection對象進行事務管理的,默認是自動提交事務,能夠手工將自動提交關閉,經過commit方法進行提交,rollback方法進行回滾,若是不提交,則數據不會真正的插入到數據庫中。Hibernate中則是經過Transaction進行事務管理,處理方法與JDBC中相似。Spring中也有本身的事務管理機制,通常是使用TransactionMananger進行管理,能夠經過Spring的注入完成此功能。
我通俗的理解以下:spring只是控制數據庫的事務提交和回滾,藉助於java的反射機制,在事務控制的方法(一般是service層的方法)先後獲取事務開啓session,而後執行你的數據操做,若是你的方法內有異常被拋出,spring會捕獲異常並回滾你在這個方法內全部的數據操做,若是成功則提交全部的數據,最後spring會幫你關閉須要關閉的東西。因此spring想要作的是,要程序員專一於寫邏輯,不須要關心數據庫什麼時候開啓和關閉鏈接。
再說的通俗點兒:事務,對於一件事,對了就提交,錯了就回滾,何時回滾,都是事務要作的事情。具體的操做由spring 配置來管理(同時你也能夠脫離框架,本身寫事務管理方法)。
使用Spring事務的優勢?
在SSH框假中Spring充當了管理容器的角色。咱們都知道Hibernate用來作持久層,由於它將JDBC作了一個良好的封裝,程序員在與數據庫進行交互時能夠不用書寫大量的SQL語句。Struts是用來作應用層的,他它負責調用業務邏輯serivce層。因此SSH框架的流程大體是:Jsp頁面----Struts------Service(業務邏輯處理類)---Hibernate(左到右)。struts負責控制Service(業務邏輯處理類),從而控制了Service的生命週期,這樣層與層之間的依賴很強,屬於耦合。這時,使用spring框架就起到了控制Action對象(Strus中的)和Service類的做用,二者之間的關係就鬆散了,Spring的Ioc機制(控制反轉和依賴注入)正是用在此處。 Spring的Ioc(控制反轉和依賴注入) 控制反轉:就是由容器控制程序之間的(依賴)關係,而非傳統實現中,由程序代碼直接操控 依賴注入:組件之間的依賴關係由容器在運行期決定 ,由容器動態的將某種依賴關係注入到組件之中。 從上面咱們不難看出:從頭至尾Action僅僅是充當了Service的控制工具,這些具體的業務方法是怎樣實現的,他根本就不會管,也不會問,他只要知道這些業務實現類所提供的方法接口就能夠了。而在以往單獨使用Struts框架的時候,全部的業務方法類的生命週期,甚至是一些業務流程都是由Action來控制的。層與層之間耦合性太緊密了,既下降了數據訪問的效率又使業務邏輯看起來很複雜,代碼量也不少。Spring容器控制全部Action對象和業務邏輯類的生命週期,因爲上層再也不控制下層的生命週期,層與層之間實現了徹底脫耦,使程序運行起來效率更高,維護起來也方便。 使用Spring的第二個好處(AOP應用): 事務的處理: 在以往的JDBCTemplate中事務提交成功,異常處理都是經過Try/Catch 來完成,而在Spring中。Spring容器集成了TransactionTemplate,她封裝了全部對事務處理的功能,包括異常時事務回滾,操做成功時數據提交等複雜業務功能。這都是由Spring容器來管理,大大減小了程序員的代碼量,也對事務有了很好的管理控制。Hibernate中也有對事務的管理,hibernate中事務管理是經過SessionFactory建立和維護Session來完成。而Spring對SessionFactory配置也進行了整合,不須要在經過hibernate.cfg.xml來對SessionaFactory進行設定。這樣的話就能夠很好的利用Sping對事務管理強大功能。避免了每次對數據操做都要現得到Session實例來啓動事務/提交/回滾事務還有繁瑣的Try/Catch操做。這些也就是Spring中的AOP(面向切面編程)機制很好的應用。一方面使開發業務邏輯更清晰、專業分工更加容易進行。另外一方面就是應用Spirng AOP隔離下降了程序的耦合性使咱們能夠在不一樣的應用中將各個切面結合起來使用大大提升了代碼重用度。有利於代碼重用,特別是Dao代碼的重用。事務每每和業務規則緊密關聯。當業務邏輯發生改變,意味着dao的大幅度改動。系統規模達到必定程度,修改風險至關大。Spring的好處是不更改現有的dao,僅需對現有的service bean進行配置就達到事務效果了。同時,把事務統一在service層,系統結構更清晰。
爲何說風險風大? Spring對於事務的配置有兩種方式:第一種,使用xml形式,第二種,使用註解的形式。 基於XMl方式: 優勢:能夠在後期維護的時候適當的調整事務管理模式,而且只要遵循必定的命名規範,可讓程序員沒必要關心事務。 缺點:系統越龐大,xml配置就越大。 基於註解方式:優勢:配置比較方便,程序員只要在service層代碼設置便可以實現。不須要知道系統須要多少個bean,交給容器來注入就行了。 缺點:當你要修改或刪除一個bean的時候,你沒法肯定到底有多少個其餘的bean依賴於這個bean。(解決方法:須要有嚴格的開發文檔,在修改實現時儘量繼續遵照相應的接口避免使其餘依賴於此的bean不可用)
在咱們用SSH開發項目的時候,咱們通常都是將事務設置在Service層 那麼當咱們調用Service層的一個方法的時候它可以保證咱們的這個方法中執行的全部的對數據庫的更新操做保持在一個事務中,在事務層裏面調用的這些方法要麼所有成功,要麼所有失敗。那麼事務的傳播特性也是從這裏提及的。 若是你在你的Service層的這個方法中,除了調用了Dao層的方法以外,還調用了本類的其餘的Service方法,那麼在調用其餘的 Service方法的時候,這個事務是怎麼規定的呢,我必須保證我在我方法裏掉用的這個方法與我自己的方法處在同一個事務中,不然如何保證事物的一致性。事務的傳播特性就是解決這個問題的,「事務是會傳播的」在Spring中有針對傳播特性的多種配置咱們大多數狀況下只用其中的一種:PROPGATION_REQUIRED:這個配置項的意思是說當我調用service層的方法的時候開啓一個事務(具體調用那一層的方法開始建立事務,要看你的aop的配置),那麼在調用這個service層裏面的其餘的方法的時候,若是當前方法產生了事務就用當前方法產生的事務,不然就建立一個新的事務。這個工做是由Spring來幫助咱們完成的。 之前沒有Spring幫助咱們完成事務的時候咱們必須本身手動的控制事務,例如當咱們項目中僅僅使用hibernate,而沒有集成進 spring的時候,咱們在一個service層中調用其餘的業務邏輯方法,爲了保證事物必須也要把當前的hibernate session傳遞到下一個方法中,或者採用ThreadLocal的方法,將session傳遞給下一個方法,其實都是一個目的。如今這個工做由 spring來幫助咱們完成,就可讓咱們更加的專一於咱們的業務邏輯。而不用去關心事務的問題。默認狀況下當發生RuntimeException的狀況下,事務纔會回滾,因此要注意一下。若是你在程序發生錯誤的狀況下,有本身的異常處理機制定義本身的Exception,必須從RuntimeException類繼承,這樣事務纔會回滾!
事務的特性:原子性、一致性、隔離性、持久性。
Spring事務傳播特性總結:
1.只要定義爲spring的bean就能夠對裏面的方法使用@Transactional註解。
2.Spring的事務傳播是Spring特有的。不是對底層jdbc的代理。
3.使用spring聲明式事務,spring使用AOP來支持聲明式事務,會根據事務屬性,自動在[方法調用以前決定是否開啓一個事務],並在[方法執行以後]決定事務提交或回滾事務。
4.Spring支持的PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區別:PROPAGATION_REQUIRES_NEW:二個事務沒有信賴關係,不會存在A事務的成功取決於B事務的狀況。有可能存在A提交B失敗。A失敗(好比執行到doSomeThingB的時候拋出異常)B提交,AB都提交,AB都失敗的可能。PROPAGATION_NESTED:與PROPAGATION_REQUIRES_NEW不一樣的是,內嵌事務B會信賴A。即存在A失敗B失敗。A成功,B失敗。A成功,B成功。而不存在A失敗,B成功。
5.特別注意PROPAGATION_NESTED的使用條件:使用JDBC 3.0驅動時,僅僅支持DataSourceTransactionManager做爲事務管理器。須要JDBC 驅動的java.sql.Savepoint類。有一些JTA的事務管理器實現可能也提供了一樣的功能。使用PROPAGATION_NESTED,還須要把PlatformTransactionManager的nestedTransactionAllowed屬性設爲true;而 nestedTransactionAllowed屬性值默認爲false;
6.特別注意PROPAGATION_REQUIRES_NEW的使用條件:JtaTransactionManager做爲事務管理器
補充文字來講一下上面的關於Spring的一個問題。基於xml和基於註解,固然了它們都有優缺點。咱們通俗的說,是這樣的。先來回顧一下傳統上是如何配置 Bean 並完成 Bean 之間依賴關係的創建。下面是 3 個類,它們分別是 Office、Car 和 Boss,這 3 個類須要在 Spring 容器中配置爲 Bean。
// Office.java public class Office { private String officeNo = "001"; // 省略 get/setter @Override public String toString() { return "officeNo:" + officeNo; } } // Car.java public class Car { private String brand; private double price; // 省略 get/setter @Override public String toString() { return "brand:" + brand + "," + "price:" + price; } } // Boss.java public class Boss { private Car car; private Office office; // 省略 get/setter @Override public String toString() { return "car:" + car + "/n" + "office:" + office; } }