了解 Servlet 和 Filter
Servlet(即servlet-api.jar) 是 J2EE 最重要的一部分,有了 Servlet 你就是 J2EE 了,J2EE 的其他方面的內容擇需采用。
而 Servlet 規范你需要掌握的就是 servlet 和 filter 這兩項技術。
絕大多數框架不是基於 servlet 就是基於 filter,如果它要在 Servlet 容器上運行,就永遠也脫離不開這個模型。
Servlet容器,大一點就是應用服務器,推薦 Tomcat 、或者 Jetty 這些輕量級的產品。
SpringMVC的入口是servlet:servlet是一種運行服務器端的java應用程序,具有獨立於平台和協議的特性,並且可以動態的生成web頁面,它工作在客戶端請求與服務器響應的中間層。
Struts2的入口是filter:filter是一個可以復用的代碼片段,可以用來轉換HTTP請求、響應和頭信息。Filter不像Servlet,它不能產生一個請求或者響應,它只是修改對某一資源的請求,或者修改從某一的響應。filter能夠在一個請求到達servlet之前預處理用戶請求,也可以在離開servlet時處理http響應。
為什么 Servlet 規范會有兩個包,javax.servlet 和 javax.servlet.http ?
早先設計該規范的人認為 Servlet 是一種服務模型,不一定是依賴某種網絡協議之上,因此就抽象出了一個 javax.servlet ,同時再提供一個基於 HTTP 協議上的接口擴展。
但是從實際運行這么多年來看,似乎沒有發現有在其他協議上實現的 Servlet 技術。
javax.servlet 和 javax.servlet.http 這兩個包總共加起來也不過是三十四個接口和類。你需要通過 J2EE 的 JavaDoc 文檔 熟知每個類和接口的具體意思。
特別是下面幾個接口必須熟知每個方法的意思和用途:
- HttpServlet
- ServetConfig
- ServletContext
- Filter
- FilterConfig
- FilterChain
- RequestDispatcher
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 一些 Listenser 類
再次強調 HttpServletRequest 和 HttpServletResponse 這兩個接口更應該是爛熟於心。
因為 Web 開發是離不開 HTTP 協議的,而 Servlet 規范其實就是對 HTTP 協議做面向對象的封裝,HTTP協議中的請求和響應就是對應了 HttpServletRequest 和 HttpServletResponse 這兩個接口。
你可以通過 HttpServletRequest 來獲取所有請求相關的信息,包括 URI、Cookie、Header、請求參數等等,別無它路。
因此當你使用某個框架時,你想獲取HTTP請求的相關信息,只要拿到 HttpServletRequest 實例即可。
再談談 Session
HTTP 協議里是沒有關於 Session 會話的定義,Session 是各種編程語言根據 HTTP 協議的無狀態這種特點而產生的。
其實現無非就是服務器端的一個哈希表,哈希表的Key就是傳遞給瀏覽器的名為 jsessionid 的 Cookie 值。
當需要將某個值保存到 session 時,容器會執行如下幾步:
a. 獲取 jsessionid 值,沒有的話就生成一個,也就是 request.getSession() 這個方法
b. 拿到的 HttpSession 對象實例就相當於一個哈希表,你可以往哈希表里存放數據(setAttribute)
c. 你也可以通過 getAttribute 來獲取某個值
而這個名為 jsessionid 的 Cookie 在瀏覽器關閉時會自動刪除。
把 Cookie 的 MaxAge 值設為 -1 就能達到瀏覽器關閉自動刪除的效果。
關於 JSP
任何一個 JSP 頁面在執行的時候都會編譯成一個 Servlet 類文件,
如果是 Tomcat 的話,這些生成的 java 文件會放置在 {TOMCAT}/work 目錄下對應項目的子目錄中。
在 servlet 中有一個包 javax.servlet.jsp 是跟 JSP 相關的一些接口規范定義。
JSP 比 Servlet 方便的地方在於可直接修改立即生效,不像 Servlet 修改后必須重啟容器才能生效。
因此 JSP 適合用來做視圖,而 Servlet 則適合做控制層。
struts不過是對servlet、filter的封裝而已,
hibernate也不過是對jdbc的封裝而已。
框架解決的是解耦的問題,復用的問題,分工的問題。
SSI框架總結
專注於控制層的Struts2
專注於業務邏輯方面的spring框架
專注於持久層的 iBatis
Struts2主要來源於webwork框架,在數據傳遞方面,Struts2提供了更加強大OGNL標簽功能,使其能夠通過在action中定義變量來直接與jsp頁面中的數據進行相互傳值,省去了Struts1中的formbean。
Spring功能非常的強大,比如它的控制反轉/依賴注入機制,省去了我們自己書寫工廠模式的工作;Spring對AOP支持使我們在用戶權限控制、事務處理方面節省了很多工作量;
iBatis則是一種輕量級的ORM框架,與Hibernate相比,iBatis提供了半自動化對象關系 映射的實現,開發人員需要編寫具體的sql語句,提供了更大的自由空間,為sql語句優化提供了便利。
下面這張圖就是我們所用到的這三種框架的結合體,下面對其作以簡單介紹。
在控制層,利用Strtus2標簽功能,在Action中直接與jsp頁面上的數據進行交互。
在調用業務邏輯層應用時,Struts2提供了對Sping的支持。
開發人員需要完成對struts.xml的配置工作和對各個Action類的編寫。
在業務邏輯層,利用Spring框架的依賴注入實現對業務邏輯類和DAO類的實例托管;
在事務處理方面,利用Spring提供的面向切面的事務處理功能,使對數據的事務控制脫離於數據訪問接口實現;
在對象關系映射方面,利用Spring對數據庫連接池的托管和對iBatis框架的支持。
開發人員需要完成對數據源的配置、對不同模塊所對應的Application*.xml文件的配置,以及對業務邏輯接口的定義和業務邏輯實現的編寫。
在持久層,利用iBatis提供的半自動化對象關系映射的實現,開發人員需要編寫具體的sql語句,為系統設計提供了更大的自由空間。
另外,開發人員需要完成對SqlMapConfig.xml和*SqlMap.xml的配置,以及對DAO接口的定義和DAO接口的實現。
在各層之間進行交換的過程中,利用數據傳輸類進行數據的傳遞和交互。其中,數據傳輸類與數據庫表一一對應。
SSI框架能夠降低我們代碼的耦合度,增強了代碼的健壯性和可重用性,加快了開發速度,
但是也有一些不足之處,比如由於三種框架的配置文件較多,也給我們帶來了一些不便,特別是對於較小的應用來說更是如此。
SSI開發過程:
一:首先引入struts2 spring ibatis 各自的jar包 在此就不一一羅列了。
二:添加配置文件
我們首先從web.xml文件說起
web.xml加載過程:
1 啟動WEB項目的時候,容器(如:Tomcat)會讀他的配置文件web.xml讀兩個節點
<context-param></context-param>和<listener></listener>
2 緊接着,容器創建一個ServletContext(上下文) 這個WEB項目所有部分都將共享這個上下文
3 容器將<context-param></context-param>轉化為鍵值對並交給ServletContext
4 容器創建<listener></listener>中的類的實例,即創建監聽
5 在監聽中會有contextInitialized(ServletContextEvent args)初始化方法,在這個方法中獲得:
ServletContext = ServletContextEvent.getServletContext();
context-param的值 = ServletContext.getInitParameter("context-param的鍵");
web.xml節點加載順序
節點的加載順序與它們在web.xml文件中的先后順序無關。即不會因為filter寫在listener的前面而會先加載filter。
最終得出的結論是:listener->filter->servlet
同時還存在着這樣一種配置節點:context-param,它用於向 ServletContext 提供鍵值對,即應用程序上下文信息。
我們的 listener, filter 等在初始化時會用到這些上下文 的信息,那么context-param 配置節是不是應該寫在 listener 配置節前呢?實際上 context-param 配置節可寫在任意位置,因此真正的加載順序為:
context-param -> listener -> filter -> servlet
加載spring
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
最終結論:
web.xml 的加載順序是:[context-param -> listener -> filter -> servlet -> spring] ,而同類型節點之間的實際程序調用的時候的順序是根據對應的 mapping 的順序進行調 用的。
打開web.xml文件,根據實際需要添加如下內容
<!--上下文參數用於log4j以及spring中使用--> <context-param> <param-name>webAppRootKey</param-name> <param-value>/WEB-INF/log4j.properties</param-value> </context-param> <!--應用程序上下文參數,指定spring配置文件位置--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/beans.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <!--監聽器 用於初始化spring框架--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
在這說說SSI整合時的一些配置文件:
1,contextConfigLocation:Spring容器啟動時需要加載Spring的配置文件。默認是/WEB-INF目錄下的applicationContext.xml文件
當然也可以放在classpath下,可以包括多個spring配置文件,這就得依靠contextConfigLocation
<!-- 加載spring的配置文件 如果文件名為applicationContext.xml並且是在WEB-INF目錄下 則無需此配置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/beans.xml</param-value> </context-param>
如果web.xml中沒有配置context-param,spring的配置就像如上這段代碼示例一下,自動去WEB-INF目錄下尋找applicationContext.xml。此時,如果你修改applicationContext.xml的名稱,或者移除它,再啟動服務器,你會得到如下異常信息:
1.nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/applicationContext.xml]
這證實了其默認配置。默認配置情況下spring只會去WEB-INF目錄下尋找配置文件,而不會去classpath下尋找。
如果我們不想將配置文件放在WEB-INF目錄下呢?開發中經常在src下面創建一個config目錄,用於存放配置文件。此時,對應的param-value改為:classpath:config/applicationContext.xml。
一定要加上classpath,這告訴spring去class目錄下的config目錄下面尋找配置文件。
2,如何啟動Spring容器
兩種方法,一種以listener啟動 一種以load-on-startup Servlet。
<!-- 配置spring監聽器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
第二種
<servlet> <servlet-name>context</servlet-name> <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
3,整合Struts2
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
4,Spring整合ibatis配置文件
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"> <value>classpath:SqlMapConfig.xml</value> </property> </bean>
5,Struts.xml
<constant name="struts.objectFactory" value="spring" />
constant配置struts的常量(也可在struts.properties)文件中配置,將struts的對象工廠托由spring管理。
SSM框架總結
SpringMVC可以完全替代Struts,配合注解的方式,編程非常快捷,而且通過restful風格定義url,讓地址看起來非常優雅。
另外,MyBatis也可以替換hibernate,正因為MyBatis的半自動特點,我們程序猿可以完全掌控SQL,這會讓有數據庫經驗的程序猿能開發出高效率的SQL語句,而且XML配置管理起來也非常方便。
-
SpringMVC:它用於web層,相當於controller(等價於傳統的servlet和struts的action),用來處理用戶請求。舉個例子,用戶在地址欄輸入http://網站域名/login,那么springmvc就會攔截到這個請求,並且調用controller層中相應的方法,(中間可能包含驗證用戶名和密碼的業務邏輯,以及查詢數據庫操作,但這些都不是springmvc的職責),最終把結果返回給用戶,並且返回相應的頁面(當然也可以只返回json/xml等格式數據)。springmvc就是做前面和后面過程的活,與用戶打交道!!
-
spring:太強大了,以至於我無法用一個詞或一句話來概括它。但與我們平時開發接觸最多的估計就是IOC容器,它可以裝載bean,有了這個機制,我們就不用在每次使用這個類的時候為它初始化,很少看到關鍵字new。另外spring的aop,事務管理等等都是我們經常用到的。
-
MyBatis:如果你問我它跟鼎鼎大名的Hibernate有什么區別?我只想說,他更符合我的需求。第一,它能自由控制sql,這會讓有數據庫經驗的人編寫的代碼能搞提升數據庫訪問的效率。第二,它可以使用xml的方式來組織管理我們的sql,因為一般程序出錯很多情況下是sql出錯,別人接手代碼后能快速找到出錯地方,甚至可以優化原來寫的sql。
SpringMVC與Struts2區別與比較總結
1、Struts2是類級別的攔截, 一個類對應一個request上下文,SpringMVC是方法級別的攔截,一個方法對應一個request上下文,而方法同時又跟一個url對應,所以說從架構本身上SpringMVC就容易實現restful url,而struts2的架構實現起來要費勁,因為Struts2中Action的一個方法可以對應一個url,而其類屬性卻被所有方法共享,這也就無法用注解或其他方式標識其所屬方法了。
2、由上邊原因,SpringMVC的方法之間基本上獨立的,獨享request response數據,請求數據通過參數獲取,處理結果通過ModelMap交回給框架,方法之間不共享變量,而Struts2搞的就比較亂,雖然方法之間也是獨立的,但其所有Action變量是共享的,這不會影響程序運行,卻給我們編碼 讀程序時帶來麻煩,每次來了請求就創建一個Action,一個Action對象對應一個request上下文。
3、由於Struts2需要針對每個request進行封裝,把request,session等servlet生命周期的變量封裝成一個Map,供給每個Action使用,並保證線程安全,所以在原則上,是比較耗費內存的。
4、 攔截器實現機制上,Struts2有以自己的interceptor機制,SpringMVC用的是獨立的AOP方式,這樣導致Struts2的配置文件量還是比SpringMVC大。
5、SpringMVC的入口是servlet,而Struts2是filter(這里要指出,filter和servlet是不同的。以前認為filter是servlet的一種特殊),這就導致了二者的機制不同,這里就牽涉到servlet和filter的區別了。
6、SpringMVC集成了Ajax,使用非常方便,只需一個注解@ResponseBody就可以實現,然后直接返回響應文本即可,而Struts2攔截器集成了Ajax,在Action中處理時一般必須安裝插件或者自己寫代碼集成進去,使用起來也相對不方便。
7、SpringMVC驗證支持JSR303,處理起來相對更加靈活方便,而Struts2驗證比較繁瑣,感覺太煩亂。
8、spring MVC和Spring是無縫的。從這個項目的管理和安全上也比Struts2高(當然Struts2也可以通過不同的目錄結構和相關配置做到SpringMVC一樣的效果,但是需要xml配置的地方不少)。
9、 設計思想上,Struts2更加符合OOP的編程思想, SpringMVC就比較謹慎,在servlet上擴展。
10、SpringMVC開發效率和性能高於Struts2。
11、SpringMVC可以認為已經100%零配置。
mybatis和ibatis區別
ibatis本是apache的一個開源項目,2010年這個項目由apache software foundation 遷移到了google code,並且改名為mybatis。
1、Mybatis實現了接口綁定,使用更加方便。
在ibatis2.x中我們需要在DAO的實現類中指定具體對應哪個xml映射文件,
而Mybatis實現了DAO接口與xml映射文件的綁定,自動為我們生成接口的具體實現,使用時不需要通過SqlMapClient去指定namespace 和 sql statement id, 只需要在 sql map config 文件中指定接口的 namespace, 並且sql statement id 和 接口的名字意義對應,然后調用對一個接口即可。
注意:
雖然Mybatis支持在接口中直接使用annotation的配置方式來簡化配置,
不過強烈建議仍然使用xml配置的方式。畢竟annotation的配置方式功能有限且代碼入侵性太強。使用xml配置方式才能體現出Mybatis的優勢所在
2、對象關系映射的改進,效率更高
相信很多在使用ibatis2.x的朋友並沒有通過ibatis的xml映射文件來實現對象間的關系映射。其實也確實沒有必要那么做,因為ibatis2.x采用的是“嵌套查詢”的方式將對象之間的關系通過查詢語句的直接拼裝來實現,其效果和在DAO或Service中自行封裝是一樣的。
不過這種方式存在“N+1查詢問題”。
概括地講,N+1查詢問題可以是這樣引起的:
? 你執行了一個單獨的SQL語句來獲取結果列表(就是+1)。
? 對返回的每條記錄,你執行了一個查詢語句來為每個加載細節(就是N)。
這個問題會導致成百上千的SQL語句被執行。這通常不是期望的。
而在Mybatis中,除了兼容ibatis2.x中的“嵌套查詢”方式外,還提供了直接“嵌套結果”的方式,其效果相當於直接通過一句sql將查詢出的dto對象自動封裝成所需的對象。
具體實現方法請自行參考Mybatis官方使用手冊,不在此累述.
不過實際上這一改進所帶來的好處也是很有限的。因為這一方式在使用分頁的時候並不起作用,或者說嵌套對象的結果集是不允許進行分頁的。這一點在Mybatis框架中已經做出了明確的限制(org.apache.ibatis.executor.resultset.NestedResultSetHandler里34行),而實際項目中需要分頁的情況又特別多……
仔細一想,一對多映射確實不能通過配置文件來分頁,因為這時查詢出的記錄數並不等於實際返回對象的size,不過一對一映射為什么也不允許就不太明白了。可能是因為一對一是一對多的特例,而在設計框架的時候並沒有考慮去處理或是難於處理這一特例吧。
3、MyBatis采用功能強大的基於OGNL的表達式來消除其他元素。
熟悉struts2的人應該對OGNL表達式不會感到陌生,
MyBatis采用OGNL表達式簡化了配置文件的復雜性,使用起來更簡潔。
補充:比較遺憾的是,Mybatis的分頁繼續沿用ibatis2.x的邏輯分頁方式,依賴於JDBC的規范。大數據量時會出現性能問題,要想實現物理分頁還得自己想辦法改了。
SSM開發過程:
第一步:先在spring
文件夾里新建spring-dao.xml
文件,因為spring的配置太多,我們這里分三層,分別是dao service web。
- 讀入數據庫連接相關參數(jdbc.properties)
- 配置數據連接池
- 配置連接屬性,可以不讀配置項文件直接在這里寫死
- 配置c3p0,只配了幾個常用的
- 配置SqlSessionFactory對象(mybatis-config.xml)
- 掃描dao層接口,動態實現dao接口,也就是說不需要daoImpl,sql和參數都寫在xml文件上
第二步:接下來到service層了。在spring
文件夾里新建spring-service.xml
文件。
- 掃描service包所有注解 @Service
- 配置事務管理器,把事務管理交由spring來完成
- 配置基於注解的聲明式事務,可以直接在方法上@Transaction
第三步:配置web層,在spring
文件夾里新建spring-web.xml
文件。
- 開啟SpringMVC注解模式,可以使用@RequestMapping,@PathVariable,@ResponseBody等
- 對靜態資源處理,如js,css,jpg等
- 配置jsp 顯示ViewResolver,例如在controller中某個方法返回一個string類型的”login”,實際上會返回”/WEB-INF/login.jsp”
- 掃描web層 @Controller
第四步:最后就是修改web.xml
文件了,它在webapp
的WEB-INF
下。
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true"> <!-- 如果是用mvn命令生成的xml,需要修改servlet版本為3.1 --> <!-- 配置DispatcherServlet --> <servlet> <servlet-name>seckill-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置springMVC需要加載的配置文件 spring-dao.xml,spring-service.xml,spring-web.xml Mybatis - > spring -> springmvc --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-*.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>seckill-dispatcher</servlet-name> <!-- 默認匹配所有的請求 --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
第五步:Java后台邏輯代碼。