javascript
Spring MVC-01循序渐进之Model 2和MVC
- 概述
- 模型1和模型2
- 模型2架構圖
- 模型2之Servlet控制器
- Product類
- ProductForm類
- ControllerServlet類
- 視圖
- 測試應用
- Maven配置文件
- 源碼
概述
Java Web開發中有兩種設計模式
模型1:頁面中心,適合小應用的開發
模型2:基于MVC模式,是Java Web的推薦框架
本篇博文我們將通過3個不同的示例來介紹模型2和MVC模式
第一個示例為基本的模型2應用,使用Servlet控制
第二個示例引入了控制器
第三個示例則引入了驗證控件來檢驗用戶的輸入
模型1和模型2
還記得初次學習JSP,通常通過鏈接的方式進行頁面之間的跳轉,非常直接,但是如果一個JSP頁面修改了名稱,在大中型項目中會帶來很大的維護問題,因此在實際的運用中并不推薦使用模型1。
模型2基于 模型—視圖—控制器(MVC)模式,該模式是Smalltalk-80用戶交互的核心概念。
一個實現了MVC模式的應用包含模型、視圖和控制器3個模塊。
視圖負責應用的展示
模型封裝了應用的數據和業務邏輯
控制器負責接收用戶的輸入,改變模型以及調整視圖的顯示
模型2中,Servlet或者filter都可以充當控制器。 在Spring MVC 和 Struts1中送Servlet作為控制器,而Struts2中則使用一個Filter作為控制器。
大部分視圖都采用JSP作為應用的視圖,當然也有其他技術。
而模型則采用POJO(Plain Old Java Object), 是一個普通對象。
實踐中會采用一個JavaBean來持有模型狀態,并將業務邏輯放到一個Action類中。一個JavaBean必須擁有一個無參的構造函數,通過get/set來訪問參數,同時支持持久化。
模型2架構圖
我們來看下模型2應用的架構圖
我們來分析一下:
每個HTTP請求都發給控制器,請求中的URI標識出對應的action。 action代表了應用可以執行的一個操作。
一個Action的Java對象稱為Action對象,一個Action可以支持多個action(在Spring MVC以及Struts2中)或者一個action(Structs1 中)。
舉個簡單的例子: 添加產品,需要兩個action
如上所述,我們需要通過URI的方式告訴控制器來執行相應的action, 比如通過
http://domain/appName/product_input來顯示“添加產品”表單
通過如下URI
http://domain/appName/product_save來保存產品。
控制器會解析URI并調用對應的Action,然后將模型對象放到視圖可以訪問的區域(以便服務端數據可以展示在瀏覽器上),最后,控制器利用RequestDispatcher跳轉到視圖(JSP頁面),用表達式語言以及定制標簽來顯示數據。
注意:調用RequestDispatcher.forward方法并不會停止執行剩余的代碼,因此,若forward方法不是最后一行代碼,則應該顯式的返回
模型2之Servlet控制器
為了便于對模型二有個直觀的了解,我們展示一個簡單的應用。實際中模型二非常復雜。
我們的demo如下所示
http://localhost:8080/chapter02a/product_input.action
demo支持如下的兩個action
展示“添加產品”表單。該action發送如上圖中輸入表單到瀏覽器,其對應的URI應包含字符串product_input
保存產品并返回如下圖所示的完成頁面,對應的URI必須包含字符串product_save
http://localhost:8080/chapter02a/product_save.action
工程結構:
- 一個Product類,作為product的領域對象
- 一個ProductForm類,封裝了HTML表單的輸入項
- 一個ControllerServlet類,控制器
- 兩個JSP頁面作為View(都在WEB-INF下確保無法直接訪問到,必須通過Servlet來跳轉)
- 一個CSS文件,定義了頁面的顯示風格
Product類
Product實例是一個封裝了產品信息的JavaBean.
包含3個屬性,name、description、price
Product類實現了Serializable接口,其實例可以安全的將數據保存到HTTPSession中。
ProductForm類
ProductForm表單類與HTML表單相映射,是后者在服務端的代表。
ProductForm用來保存HttPServletRequest類中傳遞過來的值,因為HttPServletRequest只能是字符串,所以ProductForm類中所有的屬性都是String 。注意Product中的price是float,ProductForm是String類型
ProductForm類看上去和Product類相同,那么有沒有存在的必要呢?
實際上,表單對象會傳到ServletRequest給其他組件,類似Validator,而ServletRequest是一個Servlet層的對象不應該暴露給應用的其它層。
另外一個原因是:當數據校驗失敗時,表單對象將用于保存和展示在原始表單上的輸入。
注意:大部分情況,一個表單類不需要實現Serializable接口,因為表單對象很少保存在HttPSession中
package com.artisan.learnmvc.form;public class ProductForm {private String name;private String description;private String price;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getPrice() {return price;}public void setPrice(String price) {this.price = price;} }ControllerServlet類
ControllerServlet類繼承自javax.servlet.http.HttpServlet. 其中doGet和doPOST方法都最終調用自定義的process方法,該方法是整個Servlet控制器的核心。
按照約定,所有的Servlet類名稱都帶有servlet后綴。
package com.artisan.learnmvc.servlet;import java.io.IOException;import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import com.artisan.learnmvc.form.ProductForm; import com.artisan.learnmvc.model.Product;/*** * @author Mr.Yang* @Desc ControllerServlet類繼承自javax.servlet.http.HttpServlet. * 其中doGet和doPOST方法都最終調用自定義的process方法,該方法是整個Servlet控制器的核心。**/ public class ControllerServlet extends HttpServlet {private static final long serialVersionUID = 1579L;@Overridepublic void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {process(request, response);}@Overridepublic void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {process(request, response);}private void process(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {String uri = request.getRequestURI();/** uri is in this form: /contextName/resourceName, * for example: /chapter02a/product_input. * However, in the event of a default context, the * context name is empty, and uri has this form* /resourceName, e.g.: /product_input*/int lastIndex = uri.lastIndexOf("/");String action = uri.substring(lastIndex + 1);// execute an actionif (action.equals("product_input.action")) {// no action class, there is nothing to be done} else if (action.equals("product_save.action")) {// create formProductForm productForm = new ProductForm();// populate action propertiesproductForm.setName(request.getParameter("name"));productForm.setDescription(request.getParameter("description"));productForm.setPrice(request.getParameter("price"));// create modelProduct product = new Product();product.setName(productForm.getName());product.setDescription(productForm.getDescription());try {product.setPrice(Float.parseFloat(productForm.getPrice()));} catch (NumberFormatException e) {}// code to save product// store model in a scope variable for the viewrequest.setAttribute("product", product);}// forward to a viewString dispatchUrl = null;if (action.equals("product_input.action")) {dispatchUrl = "/WEB-INF/jsp/ProductForm.jsp";} else if (action.equals("product_save.action")) {dispatchUrl = "/WEB-INF/jsp/ProductDetails.jsp";}if (dispatchUrl != null) {RequestDispatcher rd =request.getRequestDispatcher(dispatchUrl);rd.forward(request, response);}} }如果是Servlet3.0 ,可以使用注解的方式,比如
@WebServlet(name="ControllerServlet",urlPatterns = {"/product_input","/product_save"}) public class ControllerServlet extends HttpServlet { ..... }我們來分析下ControllerServlet的process方法處理所有的輸入請求。
1. 首先是獲取請求的URI和action名稱
String uri = request.getRequestURI();int lastIndex = uri.lastIndexOf("/");String action = uri.substring(lastIndex + 1);2. 緊接著,process方法執行如下步驟
創建并根據請求參數構造一個表單對象。product_save操作涉及3個屬性 name description 和price,然后創建一個領域對象,并通過表單對象設置相應屬性。
執行針對領域對象的業務邏輯,包括持久化到數據庫中
轉發請求到視圖
詳見代碼部分。
web.xml中配置Servlet
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"version="3.0"> <servlet><servlet-name>ControllerServlet</servlet-name><servlet-class>com.artisan.learnmvc.servlet.ControllerServlet</servlet-class></servlet><servlet-mapping><servlet-name>ControllerServlet</servlet-name><url-pattern>*.action</url-pattern></servlet-mapping></web-app>視圖
兩個視圖
- ProductForm.jsp 對應product_input的操作
- ProductDetails.jsp對應product_save的操作,通過EL表達式語言訪問HTTPServletRequest中的product對象。
都采用了位于css目錄下的main.css中的CSS樣式進行控制
<!DOCTYPE HTML> <html> <head> <title>Add Product Form</title> <style type="text/css">@import url(css/main.css);</style> </head> <body><div id="global"> <form action="product_save.action" method="post"><fieldset><legend>Add a product</legend><p><label for="name">Product Name: </label><input type="text" id="name" name="name" tabindex="1"></p><p><label for="description">Description: </label><input type="text" id="description" name="description" tabindex="2"></p><p><label for="price">Price: </label><input type="text" id="price" name="price" tabindex="3"></p><p id="buttons"><input id="reset" type="reset" tabindex="4"><input id="submit" type="submit" tabindex="5" value="Add Product"></p></fieldset> </form> </div> </body> </html> <!DOCTYPE HTML> <html> <head> <title>Save Product</title> <style type="text/css">@import url(css/main.css);</style> </head> <body> <div id="global"><h4>The product has been saved.</h4><p><h5>Details:</h5>Product Name: ${product.name}<br/>Description: ${product.description}<br/>Price: ${product.price}</p> </div> </body> </html>測試應用
我們使用tomcat的8080端口,運行服務,訪問
http://localhost:8080/chapter02a/product_input.action
完成輸入后,表單提交到服務器URL上
http://localhost:8080/chapter02a/product_save.action
Maven配置文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.artisan</groupId><artifactId>chapter02a</artifactId><packaging>war</packaging><version>0.0.1-SNAPSHOT</version><name>chapter02a Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope><!-- provided 依賴只有在當JDK 或者一個容器已提供該依賴之后才使用。 例如, 如果你開發了一個web 應用,你可能在編譯 classpath 中需要可用的Servlet API 來編譯一個servlet,但是你不會想要在打包好的WAR 中包含這個Servlet API;這個Servlet API JAR 由你的應用服務器或者servlet 容器提供。已提供范圍的依賴在編譯classpath (不是運行時)可用。它們不是傳遞性的,也不會被打包。 --></dependency></dependencies><build><finalName>chapter02a</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>2.5.1</version><configuration><source>1.7</source><target>1.7</target><compilerArgument>-Xlint:all</compilerArgument><showWarnings>true</showWarnings><showDeprecation>true</showDeprecation></configuration></plugin></plugins></build> </project>源碼
代碼已提交到github
https://github.com/yangshangwei/SpringMvcTutorialArtisan
總結
以上是生活随笔為你收集整理的Spring MVC-01循序渐进之Model 2和MVC的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring OXM-XStream流化
- 下一篇: Spring MVC-02循序渐进之解耦