首先,我先大概介紹一下jpetstore的整體架構(gòu),spring的這個(gè)版本主要使用了struts+spring+ibatis的框架組合,
而在MVC層的框架,這個(gè)版本又同時(shí)提供了兩個(gè)實(shí)現(xiàn)版本,一個(gè)是struts,一個(gè)是spring 自帶的web框架,

而數(shù)據(jù)庫(kù)持久層使用的是ibatis框架,這個(gè)框架是一個(gè)SQL映射框架,輕量級(jí)而且使用非常容易,基本上會(huì)

使用JDBC的朋友看一兩個(gè)小時(shí)就會(huì)使用了。
下圖是該應(yīng)用的一個(gè)簡(jiǎn)略架構(gòu)圖,沒(méi)有什么好的工具,就大概畫了一個(gè),雖然比較簡(jiǎn)單,不過(guò)也基本可以

概括應(yīng)用的整體框架了,首先是JSP請(qǐng)求,通過(guò)struts-config.xml(這里只是根據(jù)struts來(lái)畫的,spring其實(shí)也差不多),

請(qǐng)求轉(zhuǎn)到相應(yīng)的Action中,可以看到,這里有一個(gè)BaseAction,是所有Action實(shí)現(xiàn)的父類,這個(gè)類的作用稍后再講,

然后就是每個(gè)Action通過(guò)PetStoreFacade的對(duì)象調(diào)用后臺(tái)DAO對(duì)象,從而通過(guò)ibatis進(jìn)行數(shù)據(jù)的持久操作,

這里使用了門面(Facade)模式,隔離了前臺(tái)與后臺(tái)耦合度,而PetStoreFacade就是這個(gè)門面。結(jié)合下圖,

相信大家對(duì)整個(gè)jpetstore會(huì)有個(gè)大概的了解。

 

 

好了,大概的結(jié)構(gòu)講了下,接下來(lái)我們就從代碼入手,在這里考慮到struts大家比較熟悉,因此,

本文是以struts版本來(lái)講,同時(shí)聲明,本文并不會(huì)一段段代碼詳細(xì)講述,而只是提供一個(gè)學(xué)習(xí)的參考,

大概講解一下流程,讓大家不再茫然不知從哪開(kāi)始,好了,廢話也不多說(shuō)了。

既然是WEB應(yīng)用,那當(dāng)然首先從配置文件看起,那就非web.xml莫屬了,打開(kāi)WEB-INF目錄下的web.xml,

我們挑出目前我們應(yīng)該關(guān)注的配置代碼:
代碼  
<servlet>    
   <servlet-name>petstore</servlet-name>    
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    
   <load-on-startup>2</load-on-startup>    
</servlet>    
   
<servlet>    
   <servlet-name>action</servlet-name>    
   <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>    
   <load-on-startup>3</load-on-startup>    
</servlet>    
   
<servlet-mapping>    
   <servlet-name>petstore</servlet-name>    
   <!--   
   <servlet-name>action</servlet-name>   
   -->    
   <url-pattern>*.do</url-pattern>    
</servlet-mapping>
在這里可看到兩個(gè)servlet設(shè)置,以及一個(gè)servlet mapping,第一個(gè)petstore的servlet是用于spring web框架的,

而第二個(gè)action的servlet就是用于struts的,而這個(gè)版本的mapping默認(rèn)的是使用spring web,可以看到,<servlet-name>action</servlet-name>這一行是被注釋掉了,我們要使用struts的話,那就把這個(gè)注釋去掉,

改為注釋掉<servlet-name>petstore</servlet-name>,好了,如此注釋以后,整個(gè)應(yīng)用就會(huì)以struts的框架運(yùn)行。

(這些配置是什么意思?如果你還搞不懂的話,建議你先去學(xué)學(xué)基礎(chǔ)再來(lái)研究應(yīng)用吧)

接下來(lái)我們可以打開(kāi)strutc-config.xml文件,這里就是struts的默認(rèn)配置文件,在這里可以看到struts的相關(guān)配置信息。

好了,接下來(lái)我們就以一個(gè)請(qǐng)求來(lái)講述基本的請(qǐng)求流程,以search為例,生成項(xiàng)目,再啟動(dòng),

成功之后進(jìn)入應(yīng)用的首頁(yè),我們終于可以看到久違的鸚鵡界面了,激動(dòng)吧,呵呵,在界面的右上角,

有一個(gè)Search文本框,我們就以這個(gè)Search為例子來(lái)講解,輸入一個(gè)關(guān)鍵字cat,然后點(diǎn)search,

結(jié)果出來(lái)了,這個(gè)過(guò)程的內(nèi)部是如何運(yùn)作的呢?

我們用鼠標(biāo)右鍵點(diǎn)擊頁(yè)面,然后選擇屬性,我們看到顯示的地址可能是:
http://localhost:8080/shop/searchProducts.do;jsessionid=E2D01E327B82D068FEE9D073CA33A2A3

這個(gè)地址就是我們剛才點(diǎn)擊查詢時(shí)提交的地址了,我們看到searchProducts.do這個(gè)字符串,

我們之前在web.xml里面的設(shè)置大家還記得嗎?
<servlet-mapping>
   <!--
   <servlet-name>petstore</servlet-name>
   -->
   <servlet-name>action</servlet-name>
   <url-pattern>*.do</url-pattern>
</servlet-mapping>
代表著所有以.do為結(jié)尾的請(qǐng)求,都將被名叫action的servlet處理,也就是通過(guò)struts-config配置進(jìn)行處理,那我們就去struts-config.xml里面看看,打開(kāi)struts-config.xml文件,ctrl+F彈出查詢界面,輸入searchProducts,我們就可查到
<action path="/shop/searchProducts" type="org.springframework.samples.jpetstore.web.
struts.SearchProductsAction" name="emptyForm" scope="session" validate="false">    
<forward name="success" path="/WEB-INF/jsp/struts/SearchProducts.jsp"/>    
</action>
根據(jù)以上配置,我們可以得知,剛才我們的提交將會(huì)被SearchProductsAction處理,而該action的form是emptyForm,查找emptyForm這個(gè)別名,我們可以找到它指向一個(gè)BaseActionForm,打開(kāi)這個(gè)form我們可以看到,里面只有兩個(gè)validate方法和一個(gè)addErrorIfStringEmpty方法,沒(méi)有任何的屬性,那就是說(shuō)search這個(gè)提交并沒(méi)有把我們輸入的關(guān)鍵字保存在form里面,打開(kāi)SearchProductsAction,我們看到execute方法里的第一句就是

String keyword = request.getParameter("keyword");
也就是說(shuō)我們輸入的關(guān)鍵字是以參數(shù)的形式傳入到request里面,參數(shù)名字為“keyword”,我們打開(kāi)IncludeTop.jsp,這個(gè)文件在WEB-INF/jsp/struts目錄下。

注意了,jsp目錄下分別有spring以及struts兩個(gè)目錄,這兩個(gè)目錄就是分別對(duì)應(yīng)兩個(gè)web框架的,我們使用的是struts所以jsp代碼就到struts目錄里面,為什么打開(kāi)IncludeTop.jsp呢,我們可以看到,無(wú)論我們進(jìn)入哪個(gè)頁(yè)面,search那個(gè)文本框都存在,也就是說(shuō),這個(gè)文本框是被當(dāng)作一個(gè)模板插入到所有頁(yè)面當(dāng)中去的,我們隨便打開(kāi)一個(gè)頁(yè)面,就可以看到頁(yè)面開(kāi)頭都是:

<%@ include file="IncludeTop.jsp" %>
這句就是把IncludeTop.jsp當(dāng)作頁(yè)面頭部包含進(jìn)來(lái),所以凡是包含這個(gè)頭頁(yè)面的頁(yè)面,他們的頭部都是一樣的,這也是我們?cè)陂_(kāi)發(fā)中常用的一種方式,統(tǒng)一界面的一種方式,我們也可以看到在這些文件尾部也有類似的代碼,如:

<%@ include file="IncludeBottom.jsp" %>

其作用也是一樣。打開(kāi)IncludeTop.jsp后,我們看到其中有一段代碼:

<form action="<c:url value="/shop/searchProducts.do"/>" method="post">
<input type="hidden" name="search" value="true"/>
<input name="keyword" size="14"/> <input border="0" src="../images/search.gif"

type="image" name="search"/>
</form>
這段代碼就是我們search文本框以及提交鏈接的代碼,在這里就不做詳細(xì)介紹了。

好了,接下來(lái)我們?cè)倏纯催@個(gè)action的后續(xù)代碼

if (keyword != null) {  
if (!StringUtils.hasLength(keyword)) {  
   request.setAttribute("message", "Please enter a keyword to search for,

then press the search button.");  
   return mapping.findForward("failure");  
   }  
PagedListHolder productList = new PagedListHolder(getPetStore().

searchProductList(keyword.toLowerCase()));  
productList.setPageSize(4);  
request.getSession().setAttribute("SearchProductsAction_productList", productList);  
request.setAttribute("productList", productList);  
return mapping.findForward("success");  
}

這里第一句就是判斷keyword是否為空,不為空就執(zhí)行其中的代碼,我們search的時(shí)候這個(gè)keyword肯定是不為空的,

那就是說(shuō)這段if包含的代碼就是我們search的處理代碼了,有的人也許會(huì)說(shuō),如果我不輸入關(guān)鍵字而直接點(diǎn)search呢,

這keyword不就是為空了嗎?我想這個(gè)你在此處加個(gè)斷點(diǎn),再運(yùn)行一下就知道了,雖然你沒(méi)有輸入,

但是keyword一樣不是null,它將是一個(gè)空字符串,而不是空對(duì)象。我們看到if里面還包含有一個(gè)if,

這里就是判斷keyword是否為空字符串了,StringUtils.hasLength()方式是一個(gè)工具類,

用來(lái)判斷keyword是否為空字符串,如果是空字符串就返回false,而這句判斷當(dāng)返回false時(shí),

因?yàn)榍懊嬗袀€(gè)感嘆號(hào),所以值為false就執(zhí)行被if所包含的語(yǔ)句,里面的代碼就是保存一個(gè)錯(cuò)誤信息,然后return mapping.findForward("failure");,這句的意思就不再解釋了。

現(xiàn)在假設(shè)我們正確輸入keyword,那么程序?qū)⒉粫?huì)執(zhí)行if語(yǔ)句中的代碼,直接向下執(zhí)行,我們看到


PagedListHolder productList = new PagedListHolder(getPetStore().

searchProductList(keyword.toLowerCase()));

這句代碼,PagedListHolder是spring提供的一個(gè)用來(lái)保存查詢結(jié)構(gòu)類,通常用來(lái)進(jìn)行分頁(yè)處理,

因此我們可以知道

getPetStore().searchProductList(keyword.toLowerCase())
這一句就是用來(lái)查詢,并返回查詢結(jié)果的。getPetStore()這個(gè)方法是繼承自BaseAction的,它將獲得一個(gè)PetStoreFacade的實(shí)現(xiàn),我們打開(kāi)BaseAction的代碼,可以看到如下代碼

public void setServlet(ActionServlet actionServlet) {  
       super.setServlet(actionServlet);  
       if (actionServlet != null) {  
           ServletContext servletContext = actionServlet.getServletContext();  
       WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);  
       this.petStore = (PetStoreFacade) wac.getBean("petStore");  
       }  
}

這句代碼里面最重要的一句就是

WebApplicationContext wac = WebApplicationContextUtils.

getRequiredWebApplicationContext(servletContext);
這句代碼的作用就是獲取一個(gè)WebApplicationContext對(duì)象wac,在這里我們只需要知道這個(gè)對(duì)象的作用,而不對(duì)其進(jìn)行深入研究,通過(guò)這個(gè)對(duì)象,我們可以根據(jù)spring的配置文件,獲得相應(yīng)的Bean,下面這句就是用來(lái)獲取相應(yīng)bean的語(yǔ)句:


this.petStore = (PetStoreFacade) wac.getBean("petStore");
petStore這個(gè)就是bean的id,這個(gè)petStore的bean具體是哪個(gè)類呢?我們打開(kāi)applicationContext.xml,可以找到以下配置代碼


<bean id="petStore" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">
   <property name="accountDao" ref="accountDao"/>
   <property name="categoryDao" ref="categoryDao"/>
   <property name="productDao" ref="productDao"/>
   <property name="itemDao" ref="itemDao"/>
   <property name="orderDao" ref="orderDao"/>
</bean>
從這里可以看到,petStoreFacade的具體類就是PetStoreImpl,而這個(gè)類當(dāng)中,分別通過(guò)spring IOC注入了幾個(gè)DAO的bean,這幾個(gè)DAO的配置可以在dataAccessContext-local.xml文件里面找到,我們打開(kāi)PetStoreImpl這個(gè)實(shí)現(xiàn)類,我們看到類里有一個(gè)searchProductList方法,這個(gè)方法就是我們?cè)贏ction當(dāng)中調(diào)用的方法

return this.productDao.searchProductList(keywords);
從這句代碼可以看出,這個(gè)方法是通過(guò)調(diào)用productDao的searchProductList方法來(lái)獲得結(jié)果的,

productDao這個(gè)DAO從上面的配置文件可以看出,是通過(guò)IOC容器進(jìn)行注入的,我們打開(kāi)dataAccessContext-local.xml文件,可以看到

<bean id="productDao" class="org.springframework.samples.jpetstore.dao.ibatis

.SqlMapProductDao">
   <property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
這句配置代表了productDao的實(shí)現(xiàn)類就是SqlMapProductDao,同時(shí)這個(gè)類包含有一個(gè)sqlMapClient的屬性對(duì)象,這個(gè)對(duì)象也是通過(guò)ioc注入的,再在這個(gè)配置文件里,我們可以找到如下一段代碼

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
   <property name="configLocation" value="WEB-INF/sql-map-config.xml"/>
   <property name="dataSource" ref="dataSource"/>
</bean>
這段代碼就是sqlMapClient這個(gè)bean的配置,這里的實(shí)現(xiàn)是SqlMapClientFactoryBean,它是spring專門為ibatis框架提供的一個(gè)支持,通過(guò)這個(gè)對(duì)象就可以很好的集成ibatis框架,具體的介紹可以通過(guò)spring官方文檔或者是其他一些教程獲得,在這里就不多做介紹。

好了,接下來(lái)我們知道了,實(shí)際查詢數(shù)據(jù)是通過(guò)DAO的實(shí)現(xiàn)類SqlMapProductDao進(jìn)行的,而SqlMapProductDao當(dāng)中就是通過(guò)了ibatis進(jìn)行數(shù)據(jù)的查詢,從而返回結(jié)果,這里也就不多做介紹了,大家可以通過(guò)ibatis的教程獲得ibatis的使用方法,非常的簡(jiǎn)單,search操作從前臺(tái)到后臺(tái)的大概流程就介紹到這里了。

在這個(gè)參考文章中,我并沒(méi)有對(duì)具體技術(shù)做過(guò)多的講解,那是因?yàn)楸疚闹皇亲鳛橐粋€(gè)研究jpetstore的參考,提供一個(gè)可供參考的研究流程,主要是為了那些想開(kāi)始研究jpetstore但是又不知道從哪開(kāi)始或者是不知道如何進(jìn)行研究的新人朋友們而準(zhǔn)備的,如果具體的講解每一部分,那我想將不僅僅是一篇文章就可以完成的事情,因?yàn)檫@里涉及到struts,spring,ibatis等具體的框架技術(shù),每一個(gè)框架基本都可以寫成一本書,用一篇文章來(lái)講就不太實(shí)際了,而且我個(gè)人更傾向于遇到不理解的地方的時(shí)候,多使用google來(lái)搜索,這樣能夠進(jìn)一步加深自己對(duì)問(wèn)題的理解。

好了,關(guān)于jpetstore源碼研究入門的文章就寫到這里結(jié)束了,由于本人技術(shù)和文筆有限,有錯(cuò)漏或者表達(dá)不當(dāng)?shù)牡胤秸?qǐng)不要介意,歡迎各位朋友來(lái)指正。



開(kāi)心過(guò)好每一天。。。。。