Spring MVC入門學習指南_第1頁
Spring MVC入門學習指南_第2頁
Spring MVC入門學習指南_第3頁
Spring MVC入門學習指南_第4頁
Spring MVC入門學習指南_第5頁
已閱讀5頁,還剩434頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

SpringMVC入門學習指南目錄\h第1章Spring框架\h1.1XML配置文件\h1.2Spring控制反轉容器的使用\h1.2.1通過構造器創(chuàng)建一個bean實例\h1.2.2通過工廠方法創(chuàng)建一個bean實例\h1.2.3DestroyMethod的使用\h1.2.4向構造器傳遞參數(shù)\h1.2.5Setter方式依賴注入\h1.2.6構造器方式依賴注入\h1.3小結\h第2章模型2和MVC模式\h2.1模型1介紹\h2.2模型2介紹\h2.3模型2之Servlet控制器\h2.3.1Product類\h2.3.2ProductForm類\h2.3.3ControllerServlet類\h2.3.4視圖\h2.3.5測試應用\h2.4解耦控制器代碼\h2.5校驗器\h2.6后端\h2.7小結\h第3章SpringMVC介紹\h3.1采用SpringMVC的好處\h3.2SpringMVC的DispatcherServlet\h3.3Controller接口\h3.4第一個SpringMVC應用\h3.4.1目錄結構\h3.4.2部署描述符文件和SpringMVC配置文件\h3.4.3Controller\h3.4.4View\h3.4.5測試應用\h3.5ViewResolver\h3.6小結\h第4章基于注解的控制器\h4.1SpringMVC注解類型\h4.1.1Controller注解類型\h4.1.2RequestMapping注解類型\h4.2編寫請求處理方法\h4.3應用基于注解的控制器\h4.3.1目錄結構\h4.3.2配置文件\h4.3.3Controller類\h4.3.4View\h4.3.5測試應用\h4.4應用@Autowired和@Service進行依賴注入\h4.5重定向和Flash屬性\h4.6請求參數(shù)和路徑變量\h4.7@ModelAttribute\h4.8小結\h第5章數(shù)據(jù)綁定和表單標簽庫\h5.1數(shù)據(jù)綁定概覽\h5.2表單標簽庫\h5.2.1表單標簽\h5.2.2input標簽\h5.2.3password標簽\h5.2.4hidden標簽\h5.2.5textarea標簽\h5.2.6checkbox標簽\h5.2.7radiobutton標簽\h5.2.8checkboxes標簽\h5.2.9radiobuttons標簽\h5.2.10select標簽\h5.2.11option標簽\h5.2.12options標簽\h5.2.13errors標簽\h5.3數(shù)據(jù)綁定范例\h5.3.1目錄結構\h5.3.2Domain類\h5.3.3Controller類\h5.3.4Service類\h5.3.5配置文件\h5.3.6視圖\h5.3.7測試應用\h5.4小結\h第6章轉換器和格式化\h6.1Converter\h6.2Formatter\h6.3用Registrar注冊Formatter\h6.4選擇Converter,還是Formatter\h6.5小結\h第7章驗證器\h7.1驗證概覽\h7.2Spring驗證器\h7.3ValidationUtils類\h7.4Spring的Validator范例\h7.5源文件\h7.6Controller類\h7.7測試驗證器\h7.8JSR303驗證\h7.9JSR303Validator范例\h7.10小結\h第8章表達式語言\h8.1表達式語言的語法\h8.1.1關鍵字\h8.1.2[]和.運算符\h8.1.3取值規(guī)則\h8.2訪問JavaBean\h8.3EL隱式對象\h8.3.1pageContext\h8.3.2initParam\h8.3.3param\h8.3.4paramValues\h8.3.5header\h8.3.6cookie\h8.3.7applicationScope,sessionScope,requestScope和pageScope\h8.4使用其他EL運算符\h8.4.1算術運算符\h8.4.2邏輯運算符\h8.4.3關系運算符\h8.4.4empty運算符\h8.5如何在JSP2.0及其更高版本中配置EL\h8.5.1實現(xiàn)免腳本的JSP頁面\h8.5.2禁用EL計算\h8.6小結\h第9章JSTL\h9.1下載JSTL\h9.2JSTL庫\h9.3一般行為\h9.3.1out標簽\h9.3.2set標簽\h9.3.3remove標簽\h9.4條件行為\h9.4.1if標簽\h9.4.2choose、when和otherwise標簽\h9.5遍歷行為\h9.5.1forEach標簽\h9.5.2forTokens標簽\h9.6與URL相關的行為\h9.6.1url標簽\h9.6.2redirect標簽\h9.7格式化行為\h9.7.1formatNumber標簽\h9.7.2formatDate標簽\h9.7.3timeZone標簽\h9.7.4setTimeZone標簽\h9.7.5parseNumber標簽\h9.7.6parseDate標簽\h9.8函數(shù)\h9.8.1contains函數(shù)\h9.8.2containsIgnoreCase函數(shù)\h9.8.3endsWith函數(shù)\h9.8.4escapeXml函數(shù)\h9.8.5indexOf函數(shù)\h9.8.6join函數(shù)\h9.8.7length函數(shù)\h9.8.8replace函數(shù)\h9.8.9split函數(shù)\h9.8.10startsWith函數(shù)\h9.8.11substring函數(shù)\h9.8.12substringAfter函數(shù)\h9.8.13substringBefore函數(shù)\h9.8.14toLowerCase函數(shù)\h9.8.15toUpperCase函數(shù)\h9.8.16trim函數(shù)\h9.9小結\h第10章國際化\h10.1語言區(qū)域\h10.2國際化SpringMVC應用程序\h10.2.1將文本元件隔離成屬性文件\h10.2.2選擇和讀取正確的屬性文件\h10.3告訴SpringMVC使用哪個語言區(qū)域\h10.4使用message標簽\h10.5范例\h10.6小結\h第11章上傳文件\h11.1客戶端編程\h11.2MultipartFile接口\h11.3用CommonsFileUpload上傳文件\h11.4Domain類\h11.5控制器\h11.6配置文件\h11.7JSP頁面\h11.8應用程序的測試\h11.9用Servlet3及其更高版本上傳文件\h11.10客戶端上傳\h11.11小結\h第12章下載文件\h12.1文件下載概覽\h12.2范例1:隱藏資源\h12.3范例2:防止交叉引用\h12.4小結\h附錄ATomcat\hA.1下載和配置Tomcat\hA.2啟動和終止Tomcat\hA.3定義上下文\hA.4定義資源\hA.5安裝SSL證書\h附錄BServlet\hB.1ServletAPI概覽\hB.2Servlet\hB.3編寫基礎的Servlet應用程序\hB.3.1編寫和編譯Servlet類\hB.3.2應用程序目錄結構\hB.3.3調用Servlet\hB.4ServletRequest\hB.5ServletResponse\hB.6ServletConfig\hB.7ServletContext\hB.8GenericServlet\hB.9HttpServlets\hB.9.1HttpServlet\hB.9.2HttpServletResponse\hB.10處理HTML表單\hB.11使用部署描述符\h附錄CJavaServerPages\hC.1JSP概述\hC.2注釋\hC.3隱式對象\hC.4指令\hC.4.1page指令\hC.4.2include指令\hC.5腳本元素\hC.5.1表達式\hC.5.2聲明\hC.5.3禁用腳本元素\hC.6動作\hC.6.1useBean\hC.6.2setProperty和getProperty\hC.6.3include\hC.6.4forward\hC.7錯誤處理\hC.8小結\h附錄D部署描述符\hD.1概述\hD.1.1核心元素\hD.1.2context-param\hD.1.3distributable\hD.1.4error\hD.1.5filter\hD.1.6filter-mapping\hD.1.7listener\hD.1.8locale-encoding-mapping-list和locale-encoding-mapping\hD.1.9login-config\hD.1.10mime-mapping\hD.1.11security-constraint\hD.1.12security-role\hD.1.13Servlet\hD.1.14servlet-mapping\hD.1.15session-config\hD.1.16welcome-file-list\hD.1.17JSP-SpecificElements\hD.1.18taglib\hD.1.19jsp-property-group\hD.2部署\hD.3webfragment\hD.4小結第1章Spring框架Spring框架是一個開源的企業(yè)應用開發(fā)框架,作為一個輕量級的解決方案,其包含20多個不同的模塊。本書主要關注Core和Bean,以及SpringMVC模塊。SpringMVC是Spring的一個子框架,也是本書的主題。本章主要介紹Core和Bean兩個模塊,以及它們如何提供依賴注入解決方案。為方便初學者,本書會深入討論依賴注入概念的細節(jié)。后續(xù)介紹開發(fā)MVC應用的章節(jié)將會使用到本章介紹的技能。在過去數(shù)年間,依賴注入技術作為代碼可測試性的一個解決方案已經被廣泛應用。實際上,Spring、谷歌Guice等偉大框架都采用了依賴注入技術。那么,什么是依賴注入技術?很多人在使用中并不區(qū)分依賴注入和控制反轉(IoC),盡管MartinFowler在其文章中已分析了二者的不同。

/articles/injection.html

簡單來說,依賴注入的情況如下。有兩個組件A和B,A依賴于B。假定A是一個類,且A有一個方法importantMethod使用到了B,如下:

publicclassA{

publicvoidimportantMethod(){

Bb=...//getaninstanceofB

b.usefulMethod();

...

}

...

}

要使用B,類A必須先獲得組件B的實例引用。若B是一個具體類,則可通過new關鍵字直接創(chuàng)建組件B實例。但是,如果B是接口,且有多個實現(xiàn),則問題就變得復雜了。我們固然可以任意選擇接口B的一個實現(xiàn)類,但這也意味著A的可重用性大大降低了,因為無法采用B的其他實現(xiàn)。依賴注入是這樣處理此類情景的:接管對象的創(chuàng)建工作,并將該對象的引用注入需要該對象的組件。以上述例子為例,依賴注入框架會分別創(chuàng)建對象A和對象B,將對象B注入到對象A中。為了能讓框架進行依賴注入,程序員需要編寫特定的set方法或者構建方法。例如,為了能將B注入到A中,類A會被修改成如下形式:

publicclassA{

privateBb;

publicvoidimportantMethod(){

//noneedtoworryaboutcreatingBanymore

//Bb=...//getaninstanceofB

b.usefulMethod();

...

}

publicvoidsetB(Bb){

this.b=b;

}

}

修改后的類A新增了一個set方法,該方法將會被框架調用,以注入一個B的實例。由于對象依賴由依賴注入,類A的importantMethod方法不再需要在調用B的usefulMethod方法前去創(chuàng)建一個B的實例。當然,也可以采用構造器方式注入,如下所示:

publicclassA{

privateBb;

publicA(Bb){

this.b=b;

}

publicvoidimportantMethod(){

//noneedtoworryaboutcreatingBanymore

//Bb=...//getaninstanceofB

b.usefulMethod();

...

}

}

本例中,Spring會先創(chuàng)建B的實例,再創(chuàng)建實例A,然后把B注入到實例A中。注:Spring管理的對象稱為beans。通過提供一個控制反轉容器(或者依賴注入容器),Spring為我們提供一種可以“聰明”地管理Java對象依賴關系的方法。其優(yōu)雅之處在于,程序員無需了解Spring框架的存在,更不需要引入任何Spring類型。從1.0版本開始,Spring就同時支持setter和構造器方式的依賴注入。從2.5版本開始,通過Autowired注解,Spring支持基于field方式的依賴注入,但缺點是程序必須引入org.springframework.beans.factory.annotation.Autowired,這對Spring產生了依賴,這樣,程序無法直接遷移到另一個依賴注入容器間。使用Spring,程序幾乎將所有重要對象的創(chuàng)建工作移交給Spring,并配置如何注入依賴。Spring支持XML或注解兩種配置方式。此外,還需要創(chuàng)建一個ApplicationContext對象,代表一個Spring控制反轉容器,org.springframework.context.ApplicationContext接口有多個實現(xiàn),包括ClassPathXmlApplicationContext和FileSystemXmlApplicationContext。這兩個實現(xiàn)都需要至少一個包含beans信息的XML文件。ClassPathXmlApplicationContext嘗試在類加載路徑中加載配置文件,而FileSystemXmlApplicationContext則從文件系統(tǒng)中加載。下面為從類路徑中加載config1.xml和config2.xml的ApplicationContext創(chuàng)建的一個代碼示例。

ApplicationContextcontext=newClassPathXmlApplicationContext(

newString[]{"config1.xml","config2.xml"});

可以通過調用ApplicationContext的getBean方法獲得對象。

Productproduct=context.getBean("product",Product.class);

getBean方法會查詢id為product且類型為Product的bean對象。注:理想情況下,我們僅需在測試代碼中創(chuàng)建一個ApplicationContext,應用程序本身無需處理。對于SpringMVC應用,可以通過一個SpringServlet來處理ApplicationContext,而無需直接處理。1.1XML配置文件從1.0版本開始,Spring就支持基于XML的配置,從2.5版本開始,增加了通過注解的配置支持。下面介紹如何配置XML文件。配置文件的根元素通常為:

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="/schema/beans"

xmlns:xsi="/2001/XMLSchema-instance"

xsi:schemaLocation="/schema/beans

<a>/schema/beans/spring-beans-3.0.xsd"></a>

...

</beans>

如果需要更強的Spring配置能力,可以在schemalocation屬性中添加相應的schema。配置文件可以是一份,也可以分解為多份,以支持模塊化配置。ApplicationContext的實現(xiàn)類支持讀取多份配置文件。另一種選擇是,通過一份主配置文件,將該文件導入到其他配置文件。下面是一個導入其他配置文件的示例:

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="/schema/beans"

xmlns:xsi="/2001/XMLSchema-instance"

xsi:schemaLocation="/schema/beans

<a>/schema/beans/spring-beans-3.0.xsd"></a>

<importresource="config1.xml"/>

<importresource="module2/config2.xml"/>

<importresource="/resources/config3.xml"/>

...

</beans>

bean元素的配置后面將會詳細介紹。1.2Spring控制反轉容器的使用本節(jié)主要介紹Spring如何管理bean和依賴關系。1.2.1通過構造器創(chuàng)建一個bean實例前面已經介紹,通過調用ApplicationContext的getBean方法可以獲取到一個bean的實例。下面的配置文件中定義了一個名為product的bean(見清單1.1)。清單1.1一個簡單的配置文件

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="/schema/beans"

xmlns:xsi="/2001/XMLSchema-instance"

xsi:schemaLocation="/schema/beans

<a>/schema/beans/spring-beans-3.0.xsd"></a>

<beanname="product"class="app01a.bean.Product"/>

</beans>

該bean的定義告訴Spring通過默認無參的構造器來初始化Product類。如果不存在該構造器(如果類作者重載了構造器,且沒有顯示聲明默認構造器),則Spring將拋出一個異常。注意,應采用id或者name屬性標識一個bean。為了讓Spring創(chuàng)建一個Product實例,應將bean定義的name值“product”(具體實踐中也可以是id值)和Product類型作為參數(shù)傳遞給ApplicationContext的getBean方法。

ApplicationContextcontext=

newClassPathXmlApplicationContext(

newString[]{"spring-config.xml"});

Productproduct1=context.getBean("product",Product.class);

product1.setName("Excellentsnakeoil");

System.out.println("product1:"+product1.getName());

1.2.2通過工廠方法創(chuàng)建一個bean實例除了通過類的構造器方式,Spring還同樣支持通過調用一個工廠的方法來初始化類。下面的bean定義展示了通過工廠方法來實例化java.util.Calendar。

<beanid="calendar"class="java.util.Calendar"

factory-method="getInstance"/>

本例中采用了id屬性,而非name屬性來標識bean,采用了getBean方法來獲取Calendar實例。

ApplicationContextcontext=

newClassPathXmlApplicationContext(

newString[]{"spring-config.xml"});

Calendarcalendar=context.getBean("calendar",Calendar.class);

1.2.3DestroyMethod的使用有時,我們希望一些類在被銷毀前能執(zhí)行一些方法。Spring考慮到了這樣的需求??梢栽赽ean定義中配置destroy-method屬性,來指定在銷毀前要被執(zhí)行的方法。下面的例子中,我們配置Spring通過java.util.concurrent.Executors的靜態(tài)方法newCachedThreadPool來創(chuàng)建一個java.uitl.concurrent.ExecutorService實例,并指定了destroy-method屬性值為shutdown方法。這樣,Spring會在銷毀ExecutorService實例前調用其shutdown方法。

<beanid="executorService"class="java.util.concurrent.Executors"

factory-method="newCachedThreadPool"

destroy-method="shutdown"/>

1.2.4向構造器傳遞參數(shù)Spring支持通過帶參數(shù)的構造器來初始化類(見清單1.2)。清單1.2Product類

packageapp01a.bean;

importjava.io.Serializable;

publicclassProductimplementsSerializable{

privatestaticfinallongserialVersionUID=748392348L;

privateStringname;

privateStringdescription;

privatefloatprice;

publicProduct(){

}

publicProduct(Stringname,Stringdescription,floatprice){

=name;

this.description=description;

this.price=price;

}

publicStringgetName(){

returnname;

}

publicvoidsetName(Stringname){

=name;

}

publicStringgetDescription(){

returndescription;

}

publicvoidsetDescription(Stringdescription){

this.description=description;

}

publicfloatgetPrice(){

returnprice;

}

publicvoidsetPrice(floatprice){

this.price=price;

}

}

如下定義展示了如何通過參數(shù)名傳遞參數(shù)。

<beanname="featuredProduct"class="app01a.bean.Product">

<constructor-argname="name"value="UltimateOliveOil"/>

<constructor-argname="description"

value="Thepurestoliveoilonthemarket"/>

<constructor-argname="price"value="9.95"/>

</bean>

這樣,在創(chuàng)建Product實例時,Spring會調用如下構造器:

publicProduct(Stringname,Stringdescription,floatprice){

=name;

this.description=description;

this.price=price;

}

除了通過名稱傳遞參數(shù)外,Spring還支持通過指數(shù)方式傳遞參數(shù),具體如下:

<beanname="featuredProduct2"class="app01a.bean.Product">

<constructor-argindex="0"value="UltimateOliveOil"/>

<constructor-argindex="1"

value="Thepurestoliveoilonthemarket"/>

<constructor-argindex="2"value="9.95"/>

</bean>

需要說明的是,采用這種方式,對應構造器的所有參數(shù)必須傳遞,缺一不可。1.2.5Setter方式依賴注入下面以Employee類和Address類為例,介紹setter方式依賴注入(見清單1.3和清單1.4)。清單1.3Employee類

packageapp01a.bean;

publicclassEmployee{

privateStringfirstName;

privateStringlastName;

privateAddresshomeAddress;

publicEmployee(){

}

publicEmployee(StringfirstName,StringlastName,Address

homeAddress){

this.firstName=firstName;

this.lastName=lastName;

this.homeAddress=homeAddress;

}

publicStringgetFirstName(){

returnfirstName;

}

publicvoidsetFirstName(StringfirstName){

this.firstName=firstName;

}

publicStringgetLastName(){

returnlastName;

}

publicvoidsetLastName(StringlastName){

this.lastName=lastName;

}

publicAddressgetHomeAddress(){

returnhomeAddress;

}

publicvoidsetHomeAddress(AddresshomeAddress){

this.homeAddress=homeAddress;

}

@Override

publicStringtoString(){

returnfirstName+""+lastName

+"\n"+homeAddress;

}

}

清單1.4Address類

packageapp01a.bean;

publicclassAddress{

privateStringline1;

privateStringline2;

privateStringcity;

privateStringstate;

privateStringzipCode;

privateStringcountry;

publicAddress(Stringline1,Stringline2,Stringcity,

Stringstate,StringzipCode,Stringcountry){

this.line1=line1;

this.line2=line2;

this.city=city;

this.state=state;

this.zipCode=zipCode;

this.country=country;

}

//gettersandsettersomitted

@Override

publicStringtoString(){

returnline1+"\n"

+line2+"\n"

+city+"\n"

+state+""+zipCode+"\n"

+country;

}

}

Employee依賴于Address類,可以通過如下配置來保證每個Employee實例都能包含Address實例。

<beanname="simpleAddress"class="app01a.bean.Address">

<constructor-argname="line1"value="151CornerStreet"/>

<constructor-argname="line2"value=""/>

<constructor-argname="city"value="Albany"/>

<constructor-argname="state"value="NY"/>

<constructor-argname="zipCode"value="99999"/>

<constructor-argname="country"value="US"/>

</bean>

<beanname="employee1"class="app01a.bean.Employee">

<propertyname="homeAddress"ref="simpleAddress"/>

<propertyname="firstName"value="Junior"/>

<propertyname="lastName"value="Moore"/>

</bean>

simpleAddress對象是Address類的一個實例,其通過構造器方式實例化。employee1對象則通過配置property元素來調用setter方法以設置值。需要注意的是,homeAddress屬性配置的是simpleAddress對象的引用。被引用對象的配置定義無須早于引用其對象的定義。本例中,employee1對象可以出現(xiàn)在simpleAddress對象定義之前。1.2.6構造器方式依賴注入清單1.3所示的Employee類提供了一個可以傳遞參數(shù)的構造器,我們還可以將Address對象通過構造器注入,如下所示:

<beanname="employee2"class="app01a.bean.Employee">

<constructor-argname="firstName"value="Senior"/>

<constructor-argname="lastName"value="Moore"/>

<constructor-argname="homeAddress"ref="simpleAddress"/>

</bean>

<beanname="simpleAddress"class="app01a.bean.Address">

<constructor-argname="line1"value="151CornerStreet"/>

<constructor-argname="line2"value=""/>

<constructor-argname="city"value="Albany"/>

<constructor-argname="state"value="NY"/>

<constructor-argname="zipCode"value="99999"/>

<constructor-argname="country"value="US"/>

</bean>

1.3小結本章學習了依賴注入的概念以及基于Spring容器的實踐,后續(xù)將在此基礎之上配置Spring應用。

第2章模型2和MVC模式JavaWeb應用開發(fā)中有兩種設計模型,為了方便,分別稱為模型1和模型2。模型1是頁面中心,適合于小應用開發(fā)。而模型2基于MVC模式,是JavaWeb應用的推薦架構(簡單類型的應用除外)。本章將會討論模型2,并展示3個不同示例應用。第一個應用是一個基本的模型2應用,采用Servlet作為控制器,第二個應用引入了控制器,第三個應用引入了驗證控件來校驗用戶的輸入。2.1模型1介紹第一次學習JSP,通常通過鏈接方式進行JSP頁面間的跳轉。這種方式非常直接,但在中型和大型應用中,這種方式會帶來維護上的問題。修改一個JSP頁面的名字,會導致大量頁面中的鏈接需要修正。因此,實踐中并不推薦模型1(但僅2~3個頁面的應用除外)。2.2模型2介紹模型2基于模型—視圖—控制器(MVC)模式,該模式是Smalltalk-80用戶交互的核心概念,那時還沒有設計模式的說法,當時稱為MVC范式。一個實現(xiàn)MVC模式的應用包含模型、視圖和控制器3個模塊。視圖負責應用的展示。模型封裝了應用的數(shù)據(jù)和業(yè)務邏輯??刂破髫撠熃邮沼脩糨斎耄淖兡P?,以及調整視圖的顯示。注:SteveBurbeck博士的論文:ApplicationsProgramminginSmalltalk-80(TM):HowtouseModel-View-Controller(MVC)詳細討論了MVC模式,論文地址為/users/smarch/st-docs/mvc.html。模型2中,Servlet或者Filter都可以充當控制器。幾乎所有現(xiàn)代Web框架都是模型2的實現(xiàn)。SpringMVC和Struts1使用一個Servlet作為控制器,而Struts2則使用一個Filter作為控制器。大部分都采用JSP頁面作為應用的視圖,當然也有其他技術。而模型則采用POJO(PlainOldJavaObject)。不同于EJB等,POJO是一個普通對象。實踐中會采用一個JavaBean來持有模型狀態(tài),并將業(yè)務邏輯放到一個Action類中。一個JavaBean必須擁有一個無參的構造器,通過get/set方法來訪問參數(shù),同時支持持久化。圖2.1展示了一個模型2應用的架構圖。圖2.1模型2架構圖每個HTTP請求都發(fā)送給控制器,請求中的URI標識出對應的action。action代表了應用可以執(zhí)行的一個操作。一個提供了Action的Java對象稱為action對象。一個action類可以支持多個action(在SpringMVC以及Struts2中),或者一個action(在Struts1中)。看似簡單的操作可能需要多個action。如,向數(shù)據(jù)庫添加一個產品,需要兩個action。(1)顯示一個“添加產品”的表單,以便用戶能輸入產品信息。(2)將表單信息保存到數(shù)據(jù)庫中。如前述,我們需要通過URI方式告訴控制器執(zhí)行相應的action。例如,通過發(fā)送類似如下URI,來顯示“添加產品”表單。

http://

domain

/

appName

/product_input

通過類似如下URI,來保存產品。

http://

domain

/

appName

/product_save

控制器會解析URI并調用相應的action,然后將模型對象放到視圖可以訪問的區(qū)域(以便服務端數(shù)據(jù)可以展示在瀏覽器上)。最后,控制器利用RequestDispatcher跳轉到視圖(JSP頁面)。在JSP頁面中,用表達式語言以及定制標簽顯示數(shù)據(jù)。注意:調用RequestDispatcher.forward方法并不會停止執(zhí)行剩余的代碼。因此,若forward方法不是最后一行代碼,則應顯式地返回。2.3模型2之Servlet控制器為了便于對模型2有一個直觀的了解,本節(jié)將展示一個簡單模型2應用。實踐中,模型2應用非常復雜。示例應用名為app02a,其功能設定為輸入一個產品信息。具體為:用戶填寫產品表單(圖2.2)并提交;示例應用保存產品并展示一個完成頁面,顯示已保存的產品信息(圖2.3)。圖2.2產品表單示例應用支持如下兩個action。(1)展示“添加產品”表單。該action發(fā)送圖2.2中的輸入表單到瀏覽器上,其對應的URI應包含字符串product_input。(2)保存產品并返回圖2.3所示的完成頁面,對應的URI必須包含字符串product_save。示例應用app02a由如下組件構成。(1)一個Product類,作為product的領域對象。圖2.3產品詳細頁(2)一個ProductForm類,封裝了HTML表單的輸入項。(3)一個ControllerServlet類,本示例應用的控制器。(4)一個SaveProductAction類。(5)兩個JSP頁面(ProductForm.jsp和ProductDetail.jsp)作為view。(6)一個CSS文件,定義了兩個JSP頁面的顯示風格。app02a目錄結構如圖2.4所示。圖2.4app02a目錄結構所有的JSP文件都放置在WEB-INF目錄下,因此無法被直接訪問。下面詳細介紹示例應用的每個組件。2.3.1Product類Product實例是一個封裝了產品信息的JavaBean。Product類(見清單2.1)包含3個屬性:productName、description和price。清單2.1Product類

packageapp02a.domain;

importjava.io.Serializable;

publicclassProductimplementsSerializable{

privatestaticfinallongserialVersionUID=748392348L;

privateStringname;

privateStringdescription;

privatefloatprice;

publicStringgetName(){

returnname;

}

publicvoidsetName(Stringname){

=name;

}

publicStringgetDescription(){

returndescription;

}

publicvoidsetDescription(Stringdescription){

this.description=description;

}

publicfloatgetPrice(){

returnprice;

}

publicvoidsetPrice(floatprice){

this.price=price;

}

}

Product類實現(xiàn)了java.io.Serializable接口,其實例可以安全地將數(shù)據(jù)保存到HttpSession中。根據(jù)Serializable要求,Product實現(xiàn)了一個serialVersionUID屬性。2.3.2ProductForm類表單類與HTML表單相映射,是后者在服務端的代表。ProductForm類(見清單2.2)包含了一個產品的字符串值。ProductForm類看上去同Product類相似,這就引出一個問題:ProductForm類是否有存在的必要。實際上,表單對象會傳遞ServletRequest給其他組件,類似Validator(本章后續(xù)段落會介紹)。而ServletRequest是一個Servlet層的對象,不應當暴露給應用的其他層。另一個原因是,當數(shù)據(jù)校驗失敗時,表單對象將用于保存和展示用戶在原始表單上的輸入。2.5節(jié)將會詳細介紹應如何處理。注意:大部分情況下,一個表單類不需要實現(xiàn)Serializable接口,因為表單對象很少保存在HttpSession中。清單2.2ProductForm類

packageapp02a.form;

publicclassProductForm{

privateStringname;

privateStringdescription;

privateStringprice;

publicStringgetName(){

returnname;

}

publicvoidsetName(Stringname){

=name;

}

publicStringgetDescription(){

returndescription;

}

publicvoidsetDescription(Stringdescription){

this.description=description;

}

publicStringgetPrice(){

returnprice;

}

publicvoidsetPrice(Stringprice){

this.price=price;

}

}

2.3.3ControllerServlet類ControllerServlet類(見清單2.3)繼承自javax.servlet.http.HttpServlet類。其doGet和doPost方法最終調用process方法,該方法是整個Servlet控制器的核心。可能有人好奇為何這個Servlet控制器被命名為ControllerServlet,實際上,這里遵從了一個約定:所有Servlet的類名稱都帶有servlet后綴。清單2.3ControllerServlet類

packageapp02a.servlet;

importjava.io.IOException;

importjavax.servlet.RequestDispatcher;

importjavax.servlet.ServletException;

importjavax.servlet.http.HttpServlet;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importapp02a.domain.Product;

importapp02a.form.ProductForm;

publicclassControllerServletextendsHttpServlet{

privatestaticfinallongserialVersionUID=1579L;

@Override

publicvoiddoGet(HttpServletRequestrequest,

HttpServletResponseresponse)

throwsIOException,ServletException{

process(request,response);

}

@Override

publicvoiddoPost(HttpServletRequestrequest,

HttpServletResponseresponse)

throwsIOException,ServletException{

process(request,response);

}

privatevoidprocess(HttpServletRequestrequest,

HttpServletResponseresponse)

throwsIOException,ServletException{

Stringuri=request.getRequestURI();

/*

*uriisinthisform:/contextName/resourceName,

*forexample:/app10a/product_input.

*However,intheeventofadefaultcontext,the

*contextnameisempty,andurihasthisform

*/resourceName,e.g.:/product_input

*/

intlastIndex=uri.lastIndexOf("/");

Stringaction=uri.substring(lastIndex+1);

//executeanaction

if(action.equals("product_input.action")){

//noactionclass,thereisnothingtobedone

}elseif(action.equals("product_save.action")){

//createform

ProductFormproductForm=newProductForm();

//populateactionproperties

productForm.setName(request.getParameter("name"));

productForm.setDescription(

request.getParameter("description"));

productForm.setPrice(request.getParameter("price"));

//createmodel

Productproduct=newProduct();

product.setName(productForm.getName());

product.setDescription(productForm.getDescription());

try{

product.setPrice(Float.parseFloat(

productForm.getPrice()));

}catch(NumberFormatExceptione){

}

//codetosaveproduct

//storemodelinascopevariablefortheview

request.setAttribute("product",product);

}

//forwardtoaview

StringdispatchUrl=null;

if(action.equals("product_input.action")){

dispatchUrl="/WEB-INF/jsp/ProductForm.jsp";

}elseif(action.equals("product_save.action")){

dispatchUrl="/WEB-INF/jsp/ProductDetails.jsp";

}

if(dispatchUrl!=null){

RequestDispatcherrd=

request.getRequestDispatcher(dispatchUrl);

rd.forward(request,response);

}

}

}

若基于Servlet3.0規(guī)范,則可以采用注解的方式,而無需在部署描述符中進行映射。

...

importjavax.servlet.annotation.WebServlet;

...

@WebServlet(name="ControllerServlet",urlPatterns={

"/product_input","/product_save"})

publicclassControllerServletextendsHttpServlet{

...

}

ControllerServlet的process方法處理所有輸入請求。首先是獲取請求URI和action名稱。

Stringuri=request.getRequestURI();

intlastIndex=uri.lastIndexOf("/");

Stringaction=uri.substring(lastIndex+1);

在本示例應用中,action值只會是product_input或product_save。接著,process方法執(zhí)行如下步驟。(1)創(chuàng)建并根據(jù)請求參數(shù)構建一個表單對象。product_save操作涉及3個屬性:name、description和price。然后創(chuàng)建一個領域對象,并通過表單對象設置相應屬性。(2)執(zhí)行針對領域對象的業(yè)務邏輯,包括將其持久化到數(shù)據(jù)庫中。(3)轉發(fā)請求到視圖(JSP頁面)。process方法中判斷action的if代碼塊如下:

//executeanaction

if(action.equals("product_input")){

//thereisnothingtobedone

}elseif(action.equals("product_save")){

...

//codetosaveproduct

}

對于product_input,無需任何操作,而針對product_save,則創(chuàng)建一個ProductForm對象和Product對象,并將前者的屬性值復制到后者。這個步驟中,針對空字符串的復制處理將留到稍后的“校驗器”一節(jié)處理。再次,process方法實例化SaveProductAction類,并調用其save方法。

//createform

ProductFormproductForm=newProductForm();

//populateactionproperties

productForm.setName(request.getParameter("name"));

productForm.setDescription(

request.getParameter("description"));

productForm.setPrice(request.getParameter("price"));

//createmodel

Productproduct=newProduct();

product.setName(productForm.getName());

product.setDescription(product.getDescription());

try{

product.setPrice(Float.parseFloat(

productForm.getPrice()));

}catch(NumberFormatExceptione){

}

//executeactionmethod

SaveProductActionsaveProductAction=newSaveProductAction();

saveProductAction.save(product);

//storemodelinascopevariablefortheview

request.setAttribute("product",product);

然后,將Product對象放入HttpServletRequest對象中,以便對應的視圖能訪問到。

//storeactioninascopevariablefortheview

request.setAttribute("product",product);

最后,process方法轉到視圖,如果action是product_input,則轉到ProductForm.jsp頁面,否則轉到ProductDetails.jsp頁面。

//forwardtoaview

StringdispatchUrl=null;

if(action.equals("Product_input")){

dispatchUrl="/WEB-INF/jsp/ProductForm.jsp";

}elseif(action.equals("Product_save")){

dispatchUrl="/WEB-INF/jsp/ProductDetails.jsp";

}

if(dispatchUrl!=null){

RequestDispatcherrd=request.getRequestDispatcher(dispatchUrl);

rd.forward(request,response);

}

2.3.4視圖示例應用包含兩個JSP頁面。第一個頁面ProductForm.jsp對應于product_input操作,第二個頁面ProductDetails.jsp對應于product_save操作。ProductForm.jsp以及ProductDetails.jsp頁面代碼分別見清單2.4和清單2.5。清單2.4ProductForm.jsp

<!DOCTYPEHTML>

<html>

<head>

<title>AddProductForm</title>

<styletype="text/css">@importurl(css/main.css);</style>

</head>

<body>

<divid="global">

<formaction="product_save.action"method="post">

<fieldset>

<legend>Addaproduct</legend>

<p>

<labelfor="name">ProductName:</label>

<inputtype="text"id="name"name="name"tabindex="1">

</p>

<p>

<labelfor="description">Description:</label>

<inputtype="text"id="description"name="description"tabindex="2">

</p>

<p>

<labelfor="price">Price:</label>

<inputtype="text"id="price"name="price"tabindex="3">

</p>

<pid="buttons">

<inputid="reset"type="reset"tabindex="4">

<inputid="submit"type="submit"tabindex="5"value="AddProduct">

</p>

</fieldset>

</form>

</div>

</body>

</html>

清單2.5ProductDetails.jsp

<!DOCTYPEHTML>

<html>

<head>

<title>SaveProduct</title>

<styletype="text/css">@importurl(css/main.css);</style>

</head>

<body>

<divid="global">

<h4>Theproducthasbeensaved.</h4>

<p>

<h5>Details:</h5>

ProductName:${}<br/>

Description:${product.description}<br/>

Price:$${product.price}

</p>

</div>

</body>

</html>

ProductForm.jsp頁面包含了一個HTML表單。頁面沒有采用HTML表格方式進行布局,而采用了位于css目錄下的main.css中的CSS樣式表進行控制。ProductDetails.jsp頁面通過表達式語言(EL)訪問HttpServletRequest所包含的product對象。本書第8章“表達式語言”會詳細介紹。本示例應用作為一個模型2的應用,可以通過如下幾種方式避免用戶通過瀏覽器直接訪問JSP頁面。將JSP頁面都放到WEB-INF目錄下。WEB-INF目錄下的任何文件或子目錄都受保護,無法通過瀏覽器直接訪問,但控制器依然可以轉發(fā)請求到這些頁面。利用一個servletfilter過濾JSP頁面。在部署

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論