MVC 模式和模型 2
MVC框架
一个实现 MVC 模式的应用包含模型、视图、控制器 3 个模块:
模型:封装了应用的数据和业务逻辑,负责管理系统业务数据
视图:负责应用的展示
控制器:负责与用户进行交互,接收用户输入、改变模型、调整视图的显示
基于MVC架构模式的 Java Web 开发
采用了 Servlet + JSP + JavaBean 的技术实现
基于MVC架构模式的 Java Web 开发步骤
1. 定义一系列的 Bean 来表示数据
2. 使用一个 Dispatcher Servlet 和控制类来处理用户请求
3. 在 Servlet 中填充 Bean
4. 在 Servlet 中,将 Bean 存储到 request、session、servletContext中
5. 将请求转发到 JSP 页面
6. 在 JSP 页面中,从 Bean 中提取数据。
其中 Dispatcher servlet 必须完成如下功能:
1. 根据 URI 调用相应的 action
2. 实例化正确的控制器类
3. 根据请求参数值来构造表单 bean
4. 调用控制器对象的相应方法
5. 转发到一个视图(JSP页面)
每个 HTTP 请求都发送给控制器,请求中的 URI 标识出对应的 action。action 代表了应用可以执行的一个操作。一个提供了 Action 的 Java 对象称为 action 对象。
控制器会解析 URI 并调用相应的 action,然后将模型对象放到视图可以访问的区域,以便服务器端数据可以展示在浏览器上。最后控制器利用 RequestDispatcher 跳转到视图(JSP页面),在JSP页面使用 EL 以及定制标签显示数据。
注意:调用 RequestDispatcher.forward 方法并不会停止执行剩余的代码。因此,若 forward 方法不是最后一行代码,则应显示的返回。
使用MVC模式的实例
目录结构如下
代码如下
DispatcherServlet.java
package app16c.servlet; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import app16c.controller.InputProductController; import app16c.controller.SaveProductController; @WebServlet(name = "DispatcherServlet", urlPatterns = {"/product_input.action", "/product_save.action"}) public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; public DispatcherServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { process(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } private void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String uri = request.getRequestURI(); int lastIndex = uri.lastIndexOf("/"); String action = uri.substring(lastIndex + 1); String dispatcherUrl = null; if (action.equals("product_input.action")) { InputProductController controller = new InputProductController(); dispatcherUrl = controller.handleRequest(request, response); } else if (action.equals("product_save.action")) { SaveProductController controller = new SaveProductController(); dispatcherUrl = controller.handleRequest(request, response); } if (dispatcherUrl != null) { RequestDispatcher rd = request.getRequestDispatcher(dispatcherUrl); rd.forward(request, response); } } }
Controller.java
package app16c.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface Controller { public abstract String handleRequest(HttpServletRequest request, HttpServletResponse response); }
InputProductController.java
package app16c.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class InputProductController implements Controller { @Override public String handleRequest(HttpServletRequest request, HttpServletResponse response) { return "/WEB-INF/jsp/ProductForm.jsp"; } }
SaveProductController.java
package app16c.controller; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import app16c.domain.Product; import app16c.form.ProductForm; import app16c.validator.ProductValidator; public class SaveProductController implements Controller { @Override public String handleRequest(HttpServletRequest request, HttpServletResponse response) { ProductForm productForm = new ProductForm(); productForm.setName(request.getParameter("name")); productForm.setDescription(request.getParameter("description")); productForm.setPrice(request.getParameter("price")); ProductValidator productValidator = new ProductValidator(); List<String> errors = productValidator.validate(productForm); System.out.println(errors); if (errors.isEmpty()) { Product product = new Product(); product.setName(productForm.getName()); product.setDescription(productForm.getDescription()); product.setPrice(Float.parseFloat(productForm.getPrice())); // insert code to save product to the database request.setAttribute("product", product); return "/WEB-INF/jsp/ProductDetails.jsp"; } else { request.setAttribute("errors", errors); request.setAttribute("form", productForm); return "/WEB-INF/jsp/ProductForm.jsp"; } } }
ProductValidator.java
package app16c.validator; import java.util.ArrayList; import java.util.List; import app16c.form.ProductForm; public class ProductValidator { public List<String> validate(ProductForm productForm) { List<String> errors = new ArrayList<>(); String name = productForm.getName(); if (name == null || name.trim().isEmpty()) { errors.add("Product must have a name"); } String price = productForm.getPrice(); if (price == null || price.trim().isEmpty()) { errors.add("Product must have a price"); }else { try { Float.parseFloat(price); } catch (NumberFormatException e) { errors.add("Invalid price value."); } } return errors; } }
Product.java
package app16c.domain; import java.io.Serializable; public class Product implements Serializable { // 一个JavaBean 实现该接口,其实例可以安全的将数据保存到 HttpSession 中。 private static final long serialVersionUID = 1L; private String name; private String description; private float 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 float getPrice() { return price; } public void setPrice(float price) { this.price = price; } }
ProductForm.java
package app16c.form; // 表单类与 HTML表单相映射,是HTML表单在服务端的代表 public class ProductForm { // 表单类不需要实现 Serializable 接口,因为表单对象很少保存在 HttpSession 中。 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; } }
ProductForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!-- taglib指令用来引用标签库并设置标签库的前缀 --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Add Product Form</title> <link rel="stylesheet" type="text/css" href="css/main.css" /> <!-- 外部样式表 --> </head> <body> <div id="global"> <c:if test="${requestScope.errors != null }"> <!-- EL表达式 --> <p id="errors"> Error(s)! <ul> <c:forEach var="error" items="${requestScope.errors }"> <!-- jstl 遍历 --> <li>${error }</li> </c:forEach> </ul> </p> </c:if> <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>
ProductDetails.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Save Product</title> <link rel="stylesheet" type="text/scc" href="css/main.css" /> </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>
测试结果