項目源碼分析--如此精闢

本文並非原創,但此文不放在首頁給大家看實在對不起大衆,籤於不標爲原創無法在首頁與各位分享這篇文章的原因,我將此博客轉載至此

在我看來此文博主並不止分析項目,更重要的提供了一種高效研究項目的方法給各位初學者學習。不多說了,下面是原文:

spring jpetstore研究入門-完結篇

上一文安裝篇講述瞭如何把jpetstore導入netbeans工程,現在所有已經準備就緒,接下來就可以開始進入正題了。

首先,我先大概介紹一下jpetstore的整體架構,spring的這個版本主要使用了struts+spring+ibatis的框架組合,而在MVC層的框架,這個版本又同時提供了兩個實現版本,一個是struts,一個是spring 自帶的web框架,而數據庫持久層使用的是ibatis框架,這個框架是一個SQL映射框架,輕量級而且使用非常容易,基本上會使用JDBC的朋友看一兩個小時就會使用了。
下圖是該應用的一個簡略架構圖,沒有什麼好的工具,就大概畫了一個,雖然比較簡單,不過也基本可以概括應用的整體框架了,首先是JSP請求,通過struts-config.xml(這裏只是根據struts來畫的,spring其實也差不多),請求轉到相應的Action中,可以看到,這裏有一個BaseAction,是所有Action實現的父類,這個類的作用稍後再講,然後就是每個Action通過PetStoreFacade的對象調用後臺DAO對象,從而通過ibatis進行數據的持久操作,這裏使用了門面(Facade)模式,隔離了前臺與後臺耦合度,而PetStoreFacade就是這個門面。結合下圖,相信大家對整個jpetstore會有個大概的瞭解。



好了,大概的結構講了下,接下來我們就從代碼入手,在這裏考慮到struts大家比較熟悉,因此,本文是以struts版本來講,同時聲明,本文並不會一段段代碼詳細講述,而只是提供一個學習的參考,大概講解一下流程,讓大家不再茫然不知從哪開始,好了,廢話也不多說了。

既然是WEB應用,那當然首先從配置文件看起,那就非web.xml莫屬了,打開WEB-INF目錄下的web.xml,我們挑出目前我們應該關注的配置代碼:

Java代碼  收藏代碼

    <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>  



在這裏可看到兩個servlet設置,以及一個servlet mapping,第一個petstore的servlet是用於spring web框架的,而第二個action的servlet就是用於struts的,而這個版本的mapping默認的是使用spring web,可以看到,<servlet-name>action</servlet-name>這一行是被註釋掉了,我們要使用struts的話,那就把這個註釋去掉,改爲註釋掉<servlet-name>petstore</servlet-name>,好了,如此註釋以後,整個應用就會以struts的框架運行。(這些配置是什麼意思?如果你還搞不懂的話,建議你先去學學基礎再來研究應用吧)

接下來我們可以打開strutc-config.xml文件,這裏就是struts的默認配置文件,在這裏可以看到struts的相關配置信息。

好了,接下來我們就以一個請求來講述基本的請求流程,以search爲例,生成項目,再啓動,成功之後進入應用的首頁,我們終於可以看到久違的鸚鵡界面了,激動吧,呵呵,在界面的右上角,有一個Search文本框,我們就以這個Search爲例子來講解,輸入一個關鍵字cat,然後點search,結果出來了,這個過程的內部是如何運作的呢?

我們用鼠標右鍵點擊頁面,然後選擇屬性,我們看到顯示的地址可能是:
http://localhost:8080/shop/searchProducts.do;jsessionid=E2D01E327B82D068FEE9D073CA33A2A3

這個地址就是我們剛纔點擊查詢時提交的地址了,我們看到searchProducts.do這個字符串,我們之前在web.xml裏面的設置大家還記得嗎?

Java代碼  收藏代碼

            <servlet-mapping>  
        <!--  
        <servlet-name>petstore</servlet-name>  
        -->  
        <servlet-name>action</servlet-name>  
        <url-pattern>*.do</url-pattern>  
    </servlet-mapping>  



代表着所有以.do爲結尾的請求,都將被名叫action的servlet處理,也就是通過struts-config配置進行處理,那我們就去struts-config.xml裏面看看,打開struts-config.xml文件,ctrl+F彈出查詢界面,輸入searchProducts,我們就可查到

Java代碼  收藏代碼

    <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>  



根據以上配置,我們可以得知,剛纔我們的提交將會被SearchProductsAction處理,而該action的form是emptyForm,查找emptyForm這個別名,我們可以找到它指向一個BaseActionForm,打開這個form我們可以看到,裏面只有兩個validate方法和一個addErrorIfStringEmpty方法,沒有任何的屬性,那就是說search這個提交並沒有把我們輸入的關鍵字保存在form裏面,打開SearchProductsAction,我們看到execute方法裏的第一句就是:

Java代碼  收藏代碼

    String keyword = request.getParameter("keyword");  



也就是說我們輸入的關鍵字是以參數的形式傳入到request裏面,參數名字爲“keyword”,我們打開IncludeTop.jsp,這個文件在WEB-INF/jsp/struts目錄下。

注意了,jsp目錄下分別有spring以及struts兩個目錄,這兩個目錄就是分別對應兩個web框架的,我們使用的是struts所以jsp代碼就到struts目錄裏面,爲什麼打開IncludeTop.jsp呢,我們可以看到,無論我們進入哪個頁面,search那個文本框都存在,也就是說,這個文本框是被當作一個模板插入到所有頁面當中去的,我們隨便打開一個頁面,就可以看到頁面開頭都是:

Java代碼  收藏代碼

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



這句就是把IncludeTop.jsp當作頁面頭部包含進來,所以凡是包含這個頭頁面的頁面,他們的頭部都是一樣的,這也是我們在開發中常用的一種方式,統一界面的一種方式,我們也可以看到在這些文件尾部也有類似的代碼,如:

Java代碼  收藏代碼

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



其作用也是一樣。打開IncludeTop.jsp後,我們看到其中有一段代碼:

Java代碼  收藏代碼

    <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文本框以及提交鏈接的代碼,在這裏就不做詳細介紹了。

好了,接下來我們再看看這個action的後續代碼:

Java代碼  收藏代碼

    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是否爲空,不爲空就執行其中的代碼,我們search的時候這個keyword肯定是不爲空的,那就是說這段if包含的代碼就是我們search的處理代碼了,有的人也許會說,如果我不輸入關鍵字而直接點search呢,這keyword不就是爲空了嗎?我想這個你在此處加個斷點,再運行一下就知道了,雖然你沒有輸入,但是keyword一樣不是null,它將是一個空字符串,而不是空對象。我們看到if裏面還包含有一個if,這裏就是判斷keyword是否爲空字符串了,StringUtils.hasLength()方式是一個工具類,用來判斷keyword是否爲空字符串,如果是空字符串就返回false,而這句判斷當返回false時,因爲前面有個感嘆號,所以值爲false就執行被if所包含的語句,裏面的代碼就是保存一個錯誤信息,然後return mapping.findForward("failure");,這句的意思就不再解釋了。

現在假設我們正確輸入keyword,那麼程序將不會執行if語句中的代碼,直接向下執行,我們看到
Java代碼  收藏代碼

    PagedListHolder productList = new PagedListHolder(getPetStore().searchProductList(keyword.toLowerCase()));  



這句代碼,PagedListHolder是spring提供的一個用來保存查詢結構類,通常用來進行分頁處理,因此我們可以知道

Java代碼  收藏代碼

    getPetStore().searchProductList(keyword.toLowerCase())  



這一句就是用來查詢,並返回查詢結果的。getPetStore()這個方法是繼承自BaseAction的,它將獲得一個PetStoreFacade的實現,我們打開BaseAction的代碼,可以看到如下代碼

Java代碼  收藏代碼

    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");  
            }  
    }  



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

Java代碼  收藏代碼

    WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);  



這句代碼的作用就是獲取一個WebApplicationContext對象wac,在這裏我們只需要知道這個對象的作用,而不對其進行深入研究,通過這個對象,我們可以根據spring的配置文件,獲得相應的Bean,下面這句就是用來獲取相應bean的語句:

Java代碼  收藏代碼

    this.petStore = (PetStoreFacade) wac.getBean("petStore");  



petStore這個就是bean的id,這個petStore的bean具體是哪個類呢?我們打開applicationContext.xml,可以找到以下配置代碼

Java代碼  收藏代碼

    <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,而這個類當中,分別通過spring IOC注入了幾個DAO的bean,這幾個DAO的配置可以在dataAccessContext-local.xml文件裏面找到,我們打開PetStoreImpl這個實現類,我們看到類裏有一個searchProductList方法,這個方法就是我們在Action當中調用的方法

Java代碼  收藏代碼

    return this.productDao.searchProductList(keywords);  



從這句代碼可以看出,這個方法是通過調用productDao的searchProductList方法來獲得結果的,productDao這個DAO從上面的配置文件可以看出,是通過IOC容器進行注入的,我們打開dataAccessContext-local.xml文件,可以看到

Java代碼  收藏代碼

    <bean id="productDao" class="org.springframework.samples.jpetstore.dao.ibatis.SqlMapProductDao">  
        <property name="sqlMapClient" ref="sqlMapClient"/>  
    </bean>  



這句配置代表了productDao的實現類就是SqlMapProductDao,同時這個類包含有一個sqlMapClient的屬性對象,這個對象也是通過ioc注入的,再在這個配置文件裏,我們可以找到如下一段代碼

Java代碼  收藏代碼

    <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這個bean的配置,這裏的實現是SqlMapClientFactoryBean,它是spring專門爲ibatis框架提供的一個支持,通過這個對象就可以很好的集成ibatis框架,具體的介紹可以通過spring官方文檔或者是其他一些教程獲得,在這裏就不多做介紹。

好了,接下來我們知道了,實際查詢數據是通過DAO的實現類SqlMapProductDao進行的,而SqlMapProductDao當中就是通過了ibatis進行數據的查詢,從而返回結果,這裏也就不多做介紹了,大家可以通過ibatis的教程獲得ibatis的使用方法,非常的簡單,search操作從前臺到後臺的大概流程就介紹到這裏了。

在這個參考文章中,我並沒有對具體技術做過多的講解,那是因爲本文只是作爲一個研究jpetstore的參考,提供一個可供參考的研究流程,主要是爲了那些想開始研究jpetstore但是又不知道從哪開始或者是不知道如何進行研究的新人朋友們而準備的,如果具體的講解每一部分,那我想將不僅僅是一篇文章就可以完成的事情,因爲這裏涉及到struts,spring,ibatis等具體的框架技術,每一個框架基本都可以寫成一本書,用一篇文章來講就不太實際了,而且我個人更傾向於遇到不理解的地方的時候,多使用google來搜索,這樣能夠進一步加深自己對問題的理解。

好了,關於jpetstore源碼研究入門的文章就寫到這裏結束了,由於本人技術和文筆有限,有錯漏或者表達不當的地方請不要介意,歡迎各位朋友來指正。 

原文鏈接:http://calmness.iteye.com/blog/69750


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章