您的位置:首页 > 编程语言 > Java开发

【笔记】Spring MVC学习指南(二)MVC模式

2015-09-22 00:08 417 查看
这一章跟Spring基本没有关系,权当是简单地复习了一下servlet的知识点。

示例实现了一个输入表单和输出信息的小功能,具体如下:

访问product_input.action跳转ProductForm.jsp,填写相关信息后点击提交按钮,访问product_save.action,跳转ProductDetails.jsp。









目录结构:



先来看web.xml,其中配置了servlet

<?xml version="1.0" encoding="utf-8"?>
<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-mapping>
        <servlet-name>ControllerServlet</servlet-name>
        <!-- 只匹配.action的请求,嗯... -->
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>ControllerServlet</servlet-name>
        <servlet-class>app02a.servlet.ControllerServlet</servlet-class>
    </servlet>
</web-app>


Servlet类ControllerServlet.java

package app02a.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 app02a.domain.Product;
import app02a.form.ProductForm;

public class ControllerServlet extends HttpServlet {

    private static final long serialVersionUID = 1579L;

    @Override // 养成添加Override注解的习惯,有利于避免重写失败
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        process(request, response); // 无论get还是post,最终都是调用process()
    }

    @Override
    public 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(); // eg: visit http://localhost:7676/product_input.action, uri: /product_input.action
        /*
         * uri is in this form: /contextName/resourceName, 
         * for example: /app10a/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("/"); // 获取最终action的名称,lastIndexOf(),避免/a/b/c.action有多个斜线造成的获取失败。
        String action = uri.substring(lastIndex + 1);
        // execute an action
        if (action.equals("product_input.action")) {
            // no action class, there is nothing to be done
        } else if (action.equals("product_save.action")) {
            // create form
            ProductForm productForm = new ProductForm();
            // populate action properties
            productForm.setName(request.getParameter("name"));
            productForm.setDescription(request.getParameter("description"));
            productForm.setPrice(request.getParameter("price"));
            // create model
            Product 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 view
            request.setAttribute("product", product);
        }

        // forward to a view
        String 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); // 典型的servlet跳转方式
            rd.forward(request, response);
        }
    }
}


再来看看两个类文件,先是ProductForm.java

package app02a.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;
    }
}

Product.java

package app02a.domain;

import java.io.Serializable;

public class Product implements Serializable {
    private static final long serialVersionUID = 748392348L;
    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;
    }
}


再来看看两个jsp文件,先是ProductForm.jsp

<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Add Product Form</title>
    <style type="text/css">@import url(/css/main.css);</style> <%-- /开头,根目录,另外 @import url()这个,没什么印象,需要注意--%>
</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"> <%--tabindex 属性规定元素的 tab 键控制次序(当 tab 键用于导航时,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

<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <%--依然是模板,粘贴复制的节奏--%>
<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/> <%--EL表达式,没啥好说的,不懂就看专门的章节去( ⊙ o ⊙ )啊!--%>
        Description: ${product.description}<br/>
        Price: $${product.price}
    </p>
</div>
</body>
</html>


没有涉及到Spring,纯粹复习servlet内容。

在2.4小节,介绍了“解耦控制器代码”的操作,主要就是修改了原ControllerServlet.java,将处理跳转的那部分由硬编码改为调用接口方法(新增Controller接口以及InputProductController和SaveProductController两个实现类)。目录结构变为:



ControllerServlet.java修改了部分代码,重命名为DispatcherServlet.java,具体见截图:









新增的Controller.java

package app02a.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface Controller {
    String handleRequest(HttpServletRequest request,
            HttpServletResponse response);
}


InputProductController.java

package app02a.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 app02a.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import app02a.domain.Product;
import app02a.form.ProductForm;

public class SaveProductController implements Controller {

    @Override
    public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
        ProductForm productForm = new ProductForm();
        // populate form properties
        productForm.setName(request.getParameter("name"));
        productForm.setDescription(request.getParameter("description"));
        productForm.setPrice(request.getParameter("price"));

        // create model
        Product product = new Product();
        product.setName(productForm.getName());
        product.setDescription(productForm.getDescription());
        try {
            product.setPrice(Float.parseFloat(productForm.getPrice()));
        } catch (NumberFormatException e) {
        }

        // insert code to add product to the database

        request.setAttribute("product", product);
        return "/WEB-INF/jsp/ProductDetails.jsp";
    }

}


如果根据现有情况来看,2.4节的改动无疑是增加了工作量,但把业务逻辑代码提取到独立的类中可以避免Servlet控制器的不断膨胀,利大于弊。



2.5小节对“校验器”功能做了个小的开发,原理很简单,就是新建了一个ProductValidator.java,内含validate()方法(在新的SaveProductController.java中被调用)对ProductForm对象进行属性值的判断并得出结论。以下是新增的代码部分:

ProductValidator.java

package app02a.validator;

import java.util.ArrayList;
import java.util.List;

import app02a.form.ProductForm;

public class ProductValidator {
    
    public List<String> validate(ProductForm productForm) {
        List<String> errors = new ArrayList<String>();
        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;
    }
}


新版SaveProductController.java

package app02a.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import app02a.domain.Product;
import app02a.form.ProductForm;
import app02a.validator.ProductValidator;

public class SaveProductController implements Controller {

    @Override
    public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
        ProductForm productForm = new ProductForm();
        // populate action properties
        productForm.setName(request.getParameter("name"));
        productForm.setDescription(request.getParameter("description"));
        productForm.setPrice(request.getParameter("price"));

        // validate ProductForm
        ProductValidator productValidator = new ProductValidator();
        List<String> errors = productValidator.validate(productForm);
        if (errors.isEmpty()) {
            // create Product from ProductForm
            Product product = new Product();
            product.setName(productForm.getName());
            product.setDescription(productForm.getDescription());
            product.setPrice(Float.parseFloat(productForm.getPrice()));

            // no validation error, execute action method
            // insert code to save product to the database

            // store product in a scope variable for the view
            request.setAttribute("product", product);
            return "/WEB-INF/jsp/ProductDetails.jsp";
        } else {
            // store errors and form in a scope variable for the view
            request.setAttribute("errors", errors);
            request.setAttribute("form", productForm);
            return "/WEB-INF/jsp/ProductForm.jsp";
        }
    }
}


ProductForm.jsp也做了相应更改,添加了输出错误提示的部分

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML>
<html>
<head>
<title>Add Product Form</title>
<style type="text/css">@import url(/css/main.css);</style>
</head>
<body>

<div id="global">
<c:if test="${requestScope.errors != null}">
        <p id="errors">
        Error(s)!
        <ul>
        <c:forEach var="error" items="${requestScope.errors}">
            <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>


第二章的内容就是这些,算是简单的MVC实例,没有涉及Spring
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: