SpringMVC

SpringMVC

Featured image

第1章 Spring MVC是什么

1.1 概念

模型:用于存储数据以及处理用户请求的业务逻辑。
视图:向控制器提交数据,显示模型中的数据。
控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示。

模型:一个或多个 JavaBean 对象,用于存储数据(实体模型,由 JavaBean 类创建)和处理业务逻辑(业务模型,由一般的Java类创建)。
视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据。
控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示。

image

1.2 Spring MVC 工作流程

image

  1. 用户通过浏览器向服务器发送请求,请求会被Spring MVC的前端控制器 DispatcherServlet 所拦截。
  2. DispatcherServlet 拦截到请求后,会调用 HandlerMapping 处理映射器。处理映射器根据请求URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
  3. DispatcherServlet 将请求提交到 Controller。
  4. Controller 调用业务逻辑处理后返回一个 ModelAndView 对象,该对象中包含视图名或包含模型和视图名。
  5. DispatcherServlet 根据 ModelAndView 对象寻找一个或多个合适的 ViewResolver 视图解析器。 ViewResolver 解析后,会向 DispatcherServlet 中返回具体的 View。
  6. DispatcherServlet 对View进行渲染(即将模型数据填充志视图中),渲染结果会返回给浏览器显示。

1.3 Spring MVC 接口

  1. Spring MVC 所有的请求都经过 DispatcherServlet 来统一分发,在 DispatcherServlet 将请求分发给 Controller 之前需要借助 Spring MVC 提供的 HandlerMapping 定位到具体的 Controller。
  2. HandlerMapping 接口负责完成客户请求到 Controller 映射。
  3. Controller 接口将处理用户请求,这和 Java Servlet 扮演的角色是一致的。一旦 Controller 处理完用户请求,将返回 ModelAndView 对象给 DispatcherServlet 前端控制器,ModelAndView 中包含了模型(Model)和视图(View)。
    从宏观角度考虑,DispatcherServlet 是整个 Web 应用的控制器;从微观考虑,Controller 是单个 Http 请求处理过程中的控制器,而 ModelAndView 是 Http 请求过程中返回的模型(Model)和视图(View)。
  4. ViewResolver 接口(视图解析器)在 Web 应用中负责查找 View 对象,从而将相应结果渲染给客户。

第2章 第一个Spring MVC应用

2.1 详细步骤

1) 创建 Web 项目并引入 JAR 包

spring-core-3.2.13.RELEASE.jar
spring-beans-3.2.13.RELEASE.jar
spring-context-3.2.13.RELEASE.jar
spring-expression-3.2.13.RELEASE.jar
commons-logging.1.2.jar spring-web-3.2.13.RELEASE.jar
spring-webmvc-3.2.13.RELEASE.jar
spring-aop-3.2.13.RELEASE.jar

2) 在 web.xml 文件中部署 DispatcherServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <display-name>springMVC</display-name>
    <!-- 部署 DispatcherServlet -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 表示容器再启动时立即加载servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!-- 处理所有URL -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

3) 创建 Web 应用首页

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Insert title here</title>
</head>
<body>
未注册的用户,请<a href="${pageContext.request.contextPath }/register"> 注册</a><br/>
已注册的用户,去<a href="${pageContext.request.contextPath }/login"> 登录</a></body>
</html>

4) 创建 Controller 类

package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class LoginController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest arg0,
                                      HttpServletResponse arg1) throws Exception {
        return new ModelAndView("/WEB-INF/jsp/login.jsp");
    }
}
package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class RegisterController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest arg0,
                                      HttpServletResponse arg1) throws Exception {
        return new ModelAndView("/WEB-INF/jsp/register.jsp");
    }
}

5) 创建 Spring MVC 配置文件并配置 Controller 映射信息

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- LoginController控制器类,映射到"/login" -->
    <bean name="/login" class="controller.LoginController"/>
    <!-- LoginController控制器类,映射到"/register" -->
    <bean name="/register" class="controller.RegisterController"/>
</beans>

6) 创建应用的其他页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Login</title>
</head>
<body>
Login
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Register</title>
</head>
<body>
Register
</body>
</html>

7) 发布并运行 Spring MVC 应用

2.2 视图解析器

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
    <!--前缀-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!--后缀-->
    <property name="suffix" value=".jsp"/>
</bean>

第3章 核心类与常用注解

3.1 一个示例

3.1.1 Controller 注解类型

1) 创建项目

2) 创建 Controller 类

package controller;
import org.springframework.stereotype.Controller;
/**
* “@Controller”表示 IndexController 的实例是一个控制器
*
* @Controller相当于@Controller(@Controller) 或@Controller(value="@Controller")
*/
@Controller
public class IndexController {
    // 处理请求的方法
}

3) 创建配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描控制器类,控制器类都在controller包及其子包下 -->
    <context:component-scan base-package="controller" />
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

3.1.2 RequestMapping 注解类型

3.1.2.1 方法级别注解

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * “@Controller”表示 IndexController 的实例是一个控制器
 *
 * @Controller相当于@Controller(@Controller) 或@Controller(value="@Controller")
 */
@Controller
public class IndexController {
    @RequestMapping(value = "/index/login")
    public String login() {
        /**
         * login代表逻辑视图名称,需要根据Spring MVC配置
         * 文件中internalResourceViewResolver的前缀和后缀找到对应的物理视图
         */
        return "login";
    }
    @RequestMapping(value = "/index/register")
    public String register() {
        return "register";
    }
}

http://localhost:8080/springMVCDemo02/index/login

3.1.2.2 类级别注解

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/login")
    public String login() {
        return "login";
    }
    @RequestMapping("/register")
    public String register() {
        return "register";
    }
}

http://localhost:8080/springMVCDemo02/index/login

3.2 编写请求处理方法

3.2.1 请求处理方法中常出现的参数类型

package controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/login")
    public String login(HttpSession session,HttpServletRequest request) {
        /*在对应的视图(login.jsp)中可以使用EL表达式${skey}取出session中的值*/
        session.setAttribute("skey", "session范围的值");
        session.setAttribute("rkey", "request范围的值");
        return "login";
    }
}
package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/register")
    public String register(Model model) {
        /*在对应的视图(register.jsp)中可以使用EL表达式${success}取出model中的值*/
        model.addAttribute("success", "注册成功");
        return "register";
    }
}

第4章 Spring MVC 获取参数

4.1 通过实体 Bean 接收请求参数

1) 创建首页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Insert title here</title>
</head>
<body>
没注册的用户,请<a href="${pageContext.request.contextPath }/index/register"> 注册</a><br/>
已注册的用户,去<a href="${pageContext.request.contextPath }/index/login"> 登录</a></body>
</html>

2) 完善配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描控制器类,控制器类都在controller包及其子包下 -->
    <context:component-scan base-package="controller" />
    <mvc:annotation-driven />
    <!-- annotation-driven用于简化开发的配置,注解DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter -->
    <!-- 使用resources过滤掉不需要dispatcherservlet的资源(即静态资源,例如css、js、html、images)。
        在使用resources时必须使用annotation-driven,否则resources元素会阻止任意控制器被调用 -->
    <!-- 允许css目录下的所有文件可见 -->
    <mvc:resources location="/css/" mapping="/css/**" />
    <!-- 允许html目录下的所有文件可见 -->
    <mvc:resources location="/html/" mapping="/html/**" />
    <!-- 允许images目录下的所有文件可见 -->
    <mvc:resources location="/images/" mapping="/images/**" />
    <!-- 完成视图的对应 -->
    <!-- 对转向页面的路径解析。prefix:前缀, suffix:后缀 -->
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

3) 创建 POJO 实体类

package pojo;
public class UserForm {
    private String uname; // 与请求参数名称相同
    private String upass;
    private String reupass;

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUpass() {
        return upass;
    }

    public void setUpass(String upass) {
        this.upass = upass;
    }

    public String getReupass() {
        return reupass;
    }

    public void setReupass(String reupass) {
        this.reupass = reupass;
    }
}

4) 创建 Controller 类

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/login")
    public String login() {
        return "login"; // 跳转到/WEB-INF/jsp下的login.jsp
    }
    @RequestMapping("/register")
    public String register() {
        return "register";
    }
}
package controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.UserForm;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@Controller
@RequestMapping("/user")
public class UserController {
    // 得到一个用来记录日志的对象,这样在打印信息的时候能够标记打印的是哪个类的信息
    private static final Log logger = LogFactory.getLog(UserController.class);
    /**
     * 处理登录 使用UserForm对象(实体Bean) user接收注册页面提交的请求参数
     */
    @RequestMapping("/login")
    public String login(UserForm user, HttpSession session, Model model) {
        if ("zhangsan".equals(user.getUname())
                && "123456".equals(user.getUpass())) {
            session.setAttribute("u", user);
            logger.info("成功");
            return "main"; // 登录成功,跳转到 main.jsp
        } else {
            logger.info("失败");
            model.addAttribute("messageError", "用户名或密码错误");
            return "login";
        }
    }
    /**
     * 处理注册 使用UserForm对象(实体Bean) user接收注册页面提交的请求参数
     */
    @RequestMapping("/register")
    public String register(UserForm user, Model model) {
        if ("zhangsan".equals(user.getUname())
                && "123456".equals(user.getUpass())) {
            logger.info("成功");
            return "login"; // 注册成功,跳转到 login.jsp
        } else {
            logger.info("失败");
            // 在register.jsp页面上可以使用EL表达式取出model的uname值
            model.addAttribute("uname", user.getUname());
            return "register"; // 返回 register.jsp
        }
    }
}

5) 创建页面视图

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/user/register" method="post" name="registForm">
    <table border=1 bgcolor="lightblue" align="center">
        <tr>
            <td>姓名:</td>
            <td>
                <input class="textSize" type="text" name="uname" value="${uname}" />
            </td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>
                <input class="textSize" type="password" maxlength="20" name="upass" />
            </td>
        </tr>
        <tr>
            <td>确认密码:</td>
            <td>
                <input class="textSize" type="password" maxlength="20" name="reupass" />
            </td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <input type="image" src="${pageContext.request.contextPath }/images/ok.gif" onclick="go()">
            </td>
        </tr>
        </tab1e>
</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/user/login" method="post">
    <table>
        <tr>
            <td>姓名:</td>
            <td>
                <input type="text" name="uname" class="textSize">
            </td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>
                <input type="password" name="upass" class="textsize">
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="image" src="${pageContext.request.contextPath }/images/ok.gif" onclick="go()">
                <input type="image" src="${pageContext.request.contextPath }/images/cancel.gif" onclick="cancel()">
            </td>
        </tr>
    </table>
    ${messageError }
</form>
</body>
</html>

6) 测试应用

4.2 通过处理方法的形参接收请求参数

1) 修改register方法


    /**
    * 通过形参接收请求参数,形参名称与请求参数名称完全相同
    */
@RequestMapping("/register")
public String register(String uname,String upass,Model model) {
    if ("zhangsan".equals(uname)
            && "123456".equals(upass)) {
        logger.info("成功");
        return "login"; // 注册成功,跳转到 login.jsp
    } else {
        logger.info("失败");
        // 在register.jsp页面上可以使用EL表达式取出model的uname值
        model.addAttribute("uname", uname);
        return "register"; // 返回 register.jsp
    }
}

4.3 通过 HttpServletRequest 接收请求参数

1) 修改register方法

    /**
     * 通过HttpServletRequest接收请求参数
     */
@RequestMapping("/register")
public String register(HttpServletRequest request, Model model) {
    String uname = request.getParameter("uname");
    String upass = request.getParameter("upass");
    if ("zhangsan".equals(uname)
            && "123456".equals(upass)) {
        logger.info("成功");
        return "login"; // 注册成功,跳转到 login.jsp
    } else {
        logger.info("失败");
        // 在register.jsp页面上可以使用EL表达式取出model的uname值
        model.addAttribute("uname", uname);
        return "register"; // 返回 register.jsp
    }
}

4.4 通过 @PathVariable 接收 URL 中的请求参数

4.5 通过 @RequestParam 接收请求参数

1) 修改register方法

    /**
     * 通过@RequestParam接收请求参数
     */
@RequestMapping("/register")
public String register(@RequestParam String uname,
                           @RequestParam String upass, Model model) {
    if ("zhangsan".equals(uname) && "123456".equals(upass)) {
        logger.info("成功");
        return "login"; // 注册成功,跳转到 login.jsp
    } else {
        // 在register.jsp页面上可以使用EL表达式取出model的uname值
        model.addAttribute("uname", uname);
        return "register"; // 返回 register.jsp
    }
}

4.6 通过 @ModelAttribute 接收请求参数

    /**
     * 通过@ModelAttribute接收请求参数
     */
@RequestMapping("/register")
public String register(@ModelAttribute("user") UserForm user) {
    if ("zhangsan".equals(user.getUname()) && "123456".equals(user.getUpass())) {
        logger.info("成功");
        return "login"; // 注册成功,跳转到 login.jsp
    } else {
        logger.info("失败");
        // 使用@ModelAttribute("user")与model.addAttribute("user",user)的功能相同
        //register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值
        return "register"; // 返回 register.jsp 
        // 
    }
}

第5章 Spring MVC的转发与重定向

5.1 转发过程

5.2 重定向过程

5.3 代码示例

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/login")
    public String login() {
        //转发到一个请求方法(同一个控制器类可以省略/index/)
        return "forward:/index/isLogin";
    }
    @RequestMapping("/isLogin")
    public String isLogin() {
        //重定向到一个请求方法
        return "redirect:/index/isRegister";
    }
    @RequestMapping("/isRegister")
    public String isRegister() {
        //转发到一个视图
        return "register";
    }
}

第6章 Spring MVC依赖注入及@ModelAttribute注解的使用

6.1 依赖注入详细步骤

1) 创建 service 包

package service;
import pojo.UserForm;
public interface UserService {
    boolean login(UserForm user);
    boolean register(UserForm user);
}
package service;
import org.springframework.stereotype.Service;
import pojo.UserForm;
//注解为一个服务
@Service
public class UserServiceImpl implements UserService {
    public boolean login(UserForm user) {
        if ("zhangsan".equals(user.getUname())
                && "123456".equals(user.getUpass())) {
            return true;
        }
        return false;
    }
    public boolean register(UserForm user) {
        if ("zhangsan".equals(user.getUname())
                && "123456".equals(user.getUpass())) {
            return true;
        }
        return false;
    }
}

2) 修改配置文件

<context:component-scan base-package="service"/>

3) 修改控制器类 UserController

package controller;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.UserForm;
import service.UserService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@Controller
@RequestMapping("/user")
public class UserController {
    // 得到一个用来记录日志的对象,这样在打印信息的时候能够标记打印的是哪个类的信息
    private static final Log logger = LogFactory.getLog(UserController.class);
    // 将服务依赖注入到属性userService
    @Autowired
    public UserService userService;
    /**
     * 处理登录
     */
    @RequestMapping("/login")
    public String login(UserForm user, HttpSession session, Model model) {
        if (userService.login(user)) {
            session.setAttribute("u", user);
            logger.info("成功");
            return "main"; // 登录成功,跳转到 main.jsp
        } else {
            logger.info("失败");
            model.addAttribute("messageError", "用户名或密码错误");
            return "login";
        }
    }
    /**
     * 处理注册
     */
    @RequestMapping("/register")
    public String register(@ModelAttribute("user") UserForm user) {
        if (userService.register(user)) {
            logger.info("成功");
            return "login"; // 注册成功,跳转到 login.jsp
        } else {
            logger.info("失败");
            // 使用@ModelAttribute("user")与model.addAttribute("user",user)的功能相同
            // 在register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值
            return "register"; // 返回register.jsp
        }
    }
}

6.2 @ModelAttribute注解的使用

6.2.1 绑定请求参数到实体对象(表单的命令对象)

@RequestMapping("/register")
public String register(@ModelAttribute("user") UserForm user) {
    if ("zhangsan".equals(uname) && "123456".equals(upass)) {
        logger.info("成功");
        return "login";
    } else {
        logger.info("失败");
        return "register";
}

在上述代码中“@ModelAttribute(”user”)UserForm user”语句的功能有两个:
将请求参数的输入封装到 user 对象中。
创建 UserForm 实例。

6.2.2 注解一个非请求处理方法


第7章 Spring MVC数据绑定

image

  1. Spring MVC将ServletRequest对象传递给DataBinder。
  2. 将处理方法的入参对象传递给DataBinder。
  3. DataBinder调用ConversionService组件进行数据类型转换、数据格式化等工作,并将ServletRequest对象中的消息填充到参数对象中。
  4. 调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验。
  5. 检验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数。

7.1 简单数据绑定

7.1.1 绑定默认数据类型

HttpServletRequest:通过request对象获取请求信息;
HttpServletResponse:通过response处理响应信息;
HttpSession:通过session对象得到session中存放的对象;
Model/ModelMap:Model是一个接口,ModelMap是一个接口实现,作用是将model数据填充到request域。

1) 创建项目

2) 创建 Controller 类

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;

@Controller
public class UserController {
    @RequestMapping("/selectUser")
    public String selectUser(HttpServletRequest request) {
        String id = request.getParameter("id");
        System.out.println("id="+id);
        return "success";
    }
}

3) 运行项目

id = 1

7.1.2 绑定简单数据类型

7.1.2.1 参数名相同

1) 修改Controller类

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class UserController {
    @RequestMapping("/selectUser")
    public String selectUser(Integer id) {
        System.out.println("id="+id);
        return "success";
    }
}

2) 运行项目

id = 1

7.1.2.1 参数名不同

1) 修改 Controller 类

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class UserController {
    @RequestMapping("/selectUser")
    public String selectUser(@RequestParam(value="user_id")Integer id) {
        System.out.println("id="+id);
        return "success";
    }
}

2) 运行项目

id = 1

7.1.3 绑定POJO类型

1) 创建实体类

package pojo;

public class User {
    private Integer id;       //用户id
    private String username; //用户
    private Integer password;//用户密码

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getPassword() {
        return password;
    }

    public void setPassword(Integer password) {
        this.password = password;
    }
}

2) 修改 Controller 类

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.User;

@Controller
public class UserController {
    /** 向用户注册页面跳转 */
    @RequestMapping("/toRegister")
    public String toRegister( ) {  return "register";    }

    /** 接收用户注册信息 */
    @RequestMapping("/registerUser")
    public String registerUser(User user) {
        String username = user.getUsername();
        Integer password = user.getPassword();
        System.out.println("username="+username);
        System.out.println("password="+password);
        return "success";
    }

}

3) 创建注册界面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/registerUser"
      method="post">
    用户名:<input type="text" name="username" /><br />&nbsp;&nbsp;&nbsp;码:<input type="text" name="password" /><br />
    <input type="submit" value="注册"/>
</form>
</body>
</html>

3) 运行项目

username=lam
password=123

7.1.4 绑定包装POJO类型

1) 创建实体类

package pojo;

public class Orders {
    private Integer ordersId; // 订单编号
    private User user;          // 用户POJO,所属用户
    public Integer getOrdersId() { return ordersId;  }
    public void setOrdersId(Integer ordersId) { this.ordersId = ordersId;  }
    public User getUser() { return user; }
    public void setUser(User user) { this.user = user;  }
}

2) 创建 Controller 类

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.Orders;
import pojo.User;

@Controller
public class OrdersController {
    /** 向查询订单页面跳转 */
    @RequestMapping("/toOrder")
    public String toOrder( ) {  return "order";    }

    /** 查询订单和用户信息  */
    @RequestMapping("/findOrdersWithUser")
    public String findOrdersWithUser(Orders orders) {
        Integer orderId = orders.getOrdersId();
        User user = orders.getUser();
        String username = user.getUsername();
        System.out.println("orderId=" + orderId);
        System.out.println("username=" + username);
        return "success";
    }
}

3) 创建注册界面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/findOrdersWithUser"
      method="post">
    <!-- 参数是包装类基本属性,则直接用属性名 -->
    订单编号:<input type="text" name="ordersId" /><br />
    <!-- 参数是包装类中POJO类的子属性,则必须用【对象.属性】 -->
    所属用户:<input type="text" name="user.username" /><br />
    <input type="submit" value="查询" />
</form>
</body>
</html>

4) 运行项目

orderId=1 username=lam

7.1.5 绑定自定义数据

7.1.5.1 类型转换器

7.1.5.1.1 内置的类型转换器

1) 标量转换器

名称 作用
StringToBooleanConverter String 到 boolean 类型转换
ObjectToStringConverter Object 到 String 转换,调用 toString 方法转换
StringToNumberConverterFactory String 到数字转换(例如 Integer、Long 等)
NumberToNumberConverterFactory 数字子类型(基本类型)到数字类型(包装类型)转换
StringToCharacterConverter String 到 Character 转换,取字符串中的第一个字符
NumberToCharacterConverter 数字子类型到 Character 转换
CharacterToNumberFactory Character 到数字类型转换
StringToEnumConverterFactory String 到枚举类型转换,通过 Enum.valueOf 将字符串转换为需要的枚举类型
EnumToStringConverter 枚举类型到 String 转换,返回枚举对象的 name 值
StringToLocaleConverter String 到 java.util.Locale 转换
PropertiesToStringConverter java.util.Properties 到 String 转换,默认通过 ISO-8859-1 解码
StringToPropertiesConverter String 到 java.util.Properties 转换,默认使用 ISO-8859-1 编码

2) 集合、数组相关转换器

名称 作用
ArrayToCollectionConverter 任意数组到任意集合(List、Set)转换
CollectionToArrayConverter 任意集合到任意数组转换
ArrayToArrayConverter 任意数组到任意数组转换
CollectionToCollectionConverter 集合之间的类型转换
MapToMapConverter Map之间的类型转换
ArrayToStringConverter 任意数组到 String 转换
StringToArrayConverter 字符串到数组的转换,默认通过“,”分割,且去除字符串两边的空格(trim)
ArrayToObjectConverter 任意数组到 Object 的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回数组的第一个元素并进行类型转换
ObjectToArrayConverter Object 到单元素数组转换
CollectionToStringConverter 任意集合(List、Set)到 String 转换
StringToCollectionConverter String 到集合(List、Set)转换,默认通过“,”分割,且去除字符串两边的空格(trim)
CollectionToObjectConverter 任意集合到任意 Object 的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回集合的第一个元素并进行类型转换
ObjectToCollectionConverter Object 到单元素集合的类型转换
7.1.5.1.2 自定义类型转换器

例如有一个应用 springMVCDemo03_1 希望用户在页面表单中输入信息来创建商品信息。当输入“apple,10.58,200”时表示在程序中自动创建一个 new Goods,并将“apple”值自动赋给 goodsname 属性,将“10.58”值自动赋给 goodsprice 属性,将“200”值自动赋给 goodsnumber 属性。

如果想实现上述应用,需要做以下 5 件事:

  1. 创建实体类。
  2. 创建控制器类。
  3. 创建自定义类型转换器类。
  4. 注册类型转换器。
  5. 创建相关视图。

1) 创建项目

2) 创建实体类

package pojo;
public class GoodsModel {
    private String goodsname;
    private double goodsprice;
    private int goodsnumber;

    public String getGoodsname() {
        return goodsname;
    }

    public void setGoodsname(String goodsname) {
        this.goodsname = goodsname;
    }

    public double getGoodsprice() {
        return goodsprice;
    }

    public void setGoodsprice(double goodsprice) {
        this.goodsprice = goodsprice;
    }

    public int getGoodsnumber() {
        return goodsnumber;
    }

    public void setGoodsnumber(int goodsnumber) {
        this.goodsnumber = goodsnumber;
    }
}

3) 创建 Controller 类

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import pojo.GoodsModel;
@Controller
@RequestMapping("/my")
public class ConverterController {
    @RequestMapping("/converter")
    /*
     * 使用@RequestParam
     * ("goods")接收请求参数,然后调用自定义类型转换器GoodsConverter将字符串值转换为GoodsModel的对象gm
     */
    public String myConverter(@RequestParam("goods") GoodsModel gm, Model model) {
        model.addAttribute("goods", gm);
        return "showGoods";
    }
}

4) 创建自定义类型转换器类

package converter;
import org.springframework.core.convert.converter.Converter;
import pojo.GoodsModel;

import java.util.Arrays;

public class GoodsConverter implements Converter<String, GoodsModel> {
    public GoodsModel convert(String source) {
        // 创建一个Goods实例
        GoodsModel goods = new GoodsModel();
        // 以“,”分隔
        String stringvalues[] = source.split(",");
        if (stringvalues != null && stringvalues.length == 3) {
            // 为Goods实例赋值
            System.out.println(Arrays.toString(stringvalues));
            goods.setGoodsname(stringvalues[0]);
            goods.setGoodsprice(Double.parseDouble(stringvalues[1]));
            goods.setGoodsnumber(Integer.parseInt(stringvalues[2]));
            return goods;
        } else {
            throw new IllegalArgumentException(String.format(
                    "类型转换失败, 需要格式'apple,10.58,200 ',但格式是[% s ] ", source));
        }
    }
}

5) 注册类型转换器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描控制器类,控制器类都在controller包及其子包下 -->
    <context:component-scan base-package="controller" />
    <mvc:annotation-driven conversion-service="conversionService"/>
    <!--注册类型转换器GoodsConverter-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="converter.GoodsConverter"/>
            </list>
        </property>
    </bean>
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

6) 创建相关视图

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <form action="${pageContext.request.contextPath}/my/converter" method= "post">
    请输入商品信息(格式为apple,10.58,200):
    <input type="text" name="goods" /><br>
    <input type="submit" value="提交" />
  </form>
  </body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
您创建的商品信息如下:
<!-- 使用EL表达式取出model中的goods信息 -->
商品名称为:${goods.goodsname }
商品价格为:${goods.goodsprice }
商品名称为:${goods.goodsnumber }
</body>
</html>

7.1.5.2 格式化转换器

7.1.5.2.1 内置的格式化转换器
名称 作用
NumberFormatter 实现 Number 与 String 之间的解析与格式化
CurrencyFormatter 实现 Number 与 String 之间的解析与格式化(带货币符号)
PercentFormatter 实现 Number 与 String 之间的解析与格式化(带百分数符号)
DateFormatter 实现 Date 与 String 之间的解析与格式化
7.1.5.2.2 自定义格式化转换器
public interface Formatter<T>
public T parse(String s,java.util.Locale locale)
public String print(T object,java.util.Locale locale)

如果想实现自定义格式化转换器,需要做以下 5 件事:

  1. 创建实体类;
  2. 创建控制器类;
  3. 创建自定义格式化转换器类;
  4. 注册格式化转换器;
  5. 创建相关视图。

1) 创建项目

2) 创建实体类

package pojo;
import java.util.Date;
public class GoodsModel {
    private String goodsname;
    private double goodsprice;
    private int goodsnumber;
    private Date goodsdate;

    public String getGoodsname() {
        return goodsname;
    }

    public void setGoodsname(String goodsname) {
        this.goodsname = goodsname;
    }

    public double getGoodsprice() {
        return goodsprice;
    }

    public void setGoodsprice(double goodsprice) {
        this.goodsprice = goodsprice;
    }

    public int getGoodsnumber() {
        return goodsnumber;
    }

    public void setGoodsnumber(int goodsnumber) {
        this.goodsnumber = goodsnumber;
    }

    public Date getGoodsdate() {
        return goodsdate;
    }

    public void setGoodsdate(Date goodsdate) {
        this.goodsdate = goodsdate;
    }
}

3) 创建 Controller 类

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.GoodsModel;
@Controller
@RequestMapping("/my")
public class FormatterController {
        @RequestMapping("/formatter")
    public String myConverter(GoodsModel gm, Model model) {
        model.addAttribute("goods", gm);
        return "showGoods";
    }
}

4) 创建自定义格式化转换器类

package formatter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.springframework.format.Formatter;
public class MyFormatter implements Formatter<Date> {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    public String print(Date object, Locale arg1) {
        return dateFormat.format(object);
    }
    public Date parse(String source, Locale arg1) throws ParseException {
        return dateFormat.parse(source); // Formatter只能对字符串转换
    }
}

5) 注册格式化转换器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描controller包 -->
    <context:component-scan base-package="controller" />
    <mvc:annotation-driven conversion-service="conversionService"/>
    <!--注册MyFormatter-->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="formatters">
            <list>
                <bean class="formatter.MyFormatter"/>
            </list>
        </property>
    </bean>
    <mvc:annotation-driven conversion-service="conversionService"/>
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

6) 创建相关视图

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>添加商品信息</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/my/formatter" method= "post">
  <table border=1 bgcolor="lightblue" align="center">
    <tr>
      <td>商品名称:</td>
      <td><input class="textSize" type="text" name="goodsname" /></td>
    </tr>
    <tr>
      <td>商品价格:</td>
      <td><input class="textSize" type="text" name="goodsprice" /></td>
    </tr>
    <tr>
      <td>商品数量:</td>
      <td><input class="textSize" type="text" name="goodsnumber" /></td>
    </tr>
    <tr>
      <td>商品日期:</td>
      <td><input class="textSize" type="text" name="goodsdate" />(yyyy-MM-dd)</td>
    </tr>
    <tr>
      <td colspan="2" align="center">
        <input type="submit" value="提交" />
      </td>
    </tr>
    </tab1e>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
您创建的商品信息如下:<br/>
<!-- 使用EL表达式取出Action类的属性goods的值 -->
商品名称为:${goods.goodsname }<br/>
商品价格为:${goods.goodsprice }<br/>
商品名称为:${goods.goodsnumber }<br/>
商品日期为:${goods.goodsdate}
</body>
</html>

7.2 复杂数据绑定

7.2.1 绑定数组

1) 修改UserController

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.Orders;
import pojo.User;

@Controller
public class OrdersController {
    /** 向用户页面跳转 */
    @RequestMapping("/toUser")
    public String toUser( ) {  return "user";    }

    /** 查询订单和用户信息  */
    @RequestMapping("/findOrdersWithUser")
    public String findOrdersWithUser(Orders orders) {
        Integer orderId = orders.getOrdersId();
        User user = orders.getUser();
        String username = user.getUsername();
        System.out.println("orderId=" + orderId);
        System.out.println("username=" + username);
        return "success";
    }
}

2) 创建user.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/deleteUsers" method="post">
    <table width="20%" border=1>
        <tr> <td>选择</td> <td>用户名</td> </tr>
        <tr> <td><input name="ids" value="1" type="checkbox"></td> <td>tom</td> </tr>
        <tr> <td><input name="ids" value="2" type="checkbox"></td> <td>jack</td> </tr>
        <tr> <td><input name="ids" value="3" type="checkbox"></td> <td>lucy</td> </tr>
    </table>
    <input type="submit" value="删除"/>
</form>
</body>
</html>

3) 运行项目

删除了id为1的用户!
删除了id为2的用户!
删除了id为3的用户!

7.2.2 绑定集合

1) 创建视图模型(vm或vo)

package pojo;

import java.util.List;

public class UserVO {
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
}

2) 修改UserController

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.User;
import pojo.UserVO;

import java.util.List;

@Controller
public class UserController {
    /** 向用户注册页面跳转 */
    @RequestMapping("/toUserEdit")
    public String toUserEdit( ) {  return "user_edit";    }

    /** 接收批量修改用户的方法 */
    @RequestMapping("/editUsers")
    public String editUsers(UserVO userList) {
        // 将所有用户数据封装到集合中
        List<User> users = userList.getUsers();
        // 循环输出所有用户信息
        for (User user : users) {
            // 如果接收的用户id不为空,则表示对该用户进行了修改
            if(user.getId() !=null){
                System.out.println("修改了id为"+user.getId() + "的用户名为:"+user.getUsername());
            }
        }
        return "success";    }
}

3) 创建user_edit.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/editUsers" method="post" id='formid'>
    <table width="30%" border=1>
        <tr> <td>选择</td> <td>用户名</td> </tr>
        <tr> <td> <input name="users[0].id" value="1" type="checkbox" /> </td> <td> <input name="users[0].username" value="tome" type="text" /> </td> </tr>
        <tr> <td> <input name="users[1].id" value="2" type="checkbox" /> </td> <td> <input name="users[1].username" value="jack" type="text" /> </td> </tr>
    </table>
    <input type="submit" value="修改" />
</form>
</body>
</html>

4) 运行项目

修改了id为1的用户名为:tome
修改了id为2的用户名为:jack


第8章 JSON与RESTful

8.1 JSON 概述

8.1.1 对象结构

{
    key1:value1,
    key2:value2,
    ...
}

其中,key 必须为 String 类型,value 可以是 String、Number、Object、Array 等数据类型。例如,一个 person 对象包含姓名、密码、年龄等信息,使用 JSON 的表示形式如下:

{
    "pname":"张三",
    "password":"123456",
    "page":40
}

8.1.2 数组结构

[
    value1,
    value2,
    ...
]

上述两种(对象、数组)数据结构也可以分别组合构成更加复杂的数据结构。例如,一个 student 对象包含 sno、sname、hobby 和 college 对象,其 JSON 的表示形式如下:

{
    "sno":"201802228888",
    "sname":"张三",
    "hobby":["篮球","足球"]
    "college":{
        "cname":"清华大学",
        "city":"北京"
    }
}

8.2 JSON 数据转换*

@RequestBody:用于将请求体中的数据绑定到方法的形参中,该注解应用在方法的形参上。
@ResponseBody:用于直接返回 return 对象,该注解应用在方法上。

8.3 RESTful 支持*


第9章 拦截器

9.1 拦截器的定义

  1. 实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类。
  2. 实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类。
package interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class TestInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
    }
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前(且preHandle返回true)执行");
    }
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法在控制器的处理请求方法之前执行");
        return true;
    }
}
  1. preHandle 方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
  2. postHandle 方法:该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
  3. afterCompletion 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。

9.2 拦截器的配置

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!-- 配置一个全局拦截器,拦截所有请求 -->
    <bean class="interceptor.TestInterceptor" /> 
    <mvc:interceptor>
        <!-- 配置拦截器作用的路径 -->
        <mvc:mapping path="/**" />
        <!-- 配置不需要拦截作用的路径 -->
        <mvc:exclude-mapping path="" />
        <!-- 定义<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
        <bean class="interceptor.Interceptor1" />
    </mvc:interceptor>
    <mvc:interceptor>
        <!-- 配置拦截器作用的路径 -->
        <mvc:mapping path="/gotoTest" />
        <!-- 定义在<mvc: interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
        <bean class="interceptor.Interceptor2" />
    </mvc:interceptor>
</mvc:interceptors>

在上述示例代码中,<mvc:interceptors> 元素用于配置一组拦截器,其子元素 定义的是全局拦截器,即拦截所有的请求。 <mvc:interceptor> 元素中定义的是指定路径的拦截器,其子元素 <mvc:mapping> 用于配置拦截器作用的路径,该路径在其属性 path 中定义。 如上述示例代码中,path 的属性值“/**”表示拦截所有路径,“/gotoTest”表示拦截所有以“/gotoTest”结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置。 需要注意的是,<mvc:interceptor> 元素的子元素必须按照 <mvc:mapping.../>、<mvc:exclude-mapping.../>、 的顺序配置。

9.3 拦截器的执行流程

9.3.1 单个拦截器的执行流程

1)创建应用

2)创建 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
    <!--配置DispatcherServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

3)创建 Controller 类

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class InterceptorController {
    @RequestMapping("/gotoTest")
    public String gotoTest() {
        System.out.println("正在测试拦截器,执行控制器的处理请求方法中");
        return "test";
    }
}

4)创建拦截器类

package interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class TestInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
    }
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前(且preHandle返回true)执行");
    }
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法在控制器的处理请求方法之前执行");
        return true;
    }
}

5)创建配置文件 springmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描控制器类 -->
    <context:component-scan base-package="controller" />
    <!-- 配置视图解析器 -->
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!-- 配置拦截器 -->
    <mvc:interceptors>
        <!-- 配置一个全局拦截器,拦截所有请求 -->
        <bean class="interceptor.TestInterceptor" />
    </mvc:interceptors>
</beans>

6)创建视图 JSP 文件

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
视图
<%System.out.println("视图渲染结束。"); %>
</body>
</html>

7)测试拦截器

preHandle方法在控制器的处理请求方法之前执行
正在测试拦截器,执行控制器的处理请求方法中
postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行
视图渲染结束。
afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行

9.3.2 多个拦截器的执行流程

1)创建多个拦截器

package interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class Interceptor1 implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Interceptor preHandle 方法执行");
        /** 返回true表示继续向下执行,返回false表示中断后续的操作 */
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor1 postHandle 方法执行");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("Interceptor1 afterCompletion 方法执行");
    }
}
package interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class Interceptor2 implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Interceptor2 preHandle 方法执行");
        /** 返回true表示继续向下执行,返回false表示中断后续的操作 */
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor2 postHandle 方法执行");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("Interceptor2 afterCompletion 方法执行");
    }
}

2)配置拦截器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描控制器类 -->
    <context:component-scan base-package="controller" />
    <!-- 配置视图解析器 -->
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!-- 配置拦截器 -->
    <mvc:interceptors>
        <!-- 配置一个全局拦截器,拦截所有请求 -->
        <!--<bean class="interceptor.TestInterceptor" />-->
        <mvc:interceptor>
            <!-- 配置拦截器作用的路径 -->
            <mvc:mapping path="/**"/>
            <!--定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截-->
            <bean class="interceptor.Interceptor1"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- 配置拦截器作用的路径 -->
            <mvc:mapping path="/gotoTest"/>
            <!--定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截-->
            <bean class="interceptor.Interceptor2"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

3)测试多个拦截器

Interceptor preHandle 方法执行
Interceptor2 preHandle 方法执行
正在测试拦截器,执行控制器的处理请求方法中
Interceptor2 postHandle 方法执行
Interceptor1 postHandle 方法执行
视图渲染结束。
Interceptor2 afterCompletion 方法执行
Interceptor1 afterCompletion 方法执行

9.4 拦截器实现用户登录权限验证案例

image

1)创建应用

2)创建 POJO 类

package pojo;

public class User {
    private String uname;
    private String upwd;

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUpwd() {
        return upwd;
    }

    public void setUpwd(String upwd) {
        this.upwd = upwd;
    }
}

3)创建控制器类

package controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.User;
@Controller
public class UserController {
    /**
     * 登录页面初始化
     */
    @RequestMapping("/toLogin")
    public String initLogin() {
        return "login";
    }
    /**
     * 处理登录功能
     */
    @RequestMapping("/login")
    public String login(User user, Model model, HttpSession session) {
        System.out.println(user.getUname());
        if ("zhangsan".equals(user.getUname())
                && "123456".equals(user.getUpwd())) {
            // 登录成功,将用户信息保存到session对象中
            session.setAttribute("user", user);
            // 重定向到主页面的跳转方法
            return "redirect:main";
        }
        model.addAttribute("msg", "用户名或密码错误,请重新登录! ");
        return "login";
    }
    /**
     * 跳转到主页面
     */
    @RequestMapping("/main")
    public String toMain() {
        return "main";
    }
    /**
     * 退出登录
     */
    @RequestMapping("/logout")
    public String logout(HttpSession session) {
        // 清除 session
        session.invalidate();
        return "login";
    }
}

4)创建拦截器类

package interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        // 获取请求的URL
        String url = request.getRequestURI();
        // login.jsp或登录请求放行,不拦截
        if (url.indexOf("/toLogin") >= 0 || url.indexOf("/login") >= 0) {
            return true;
        }
        // 获取 session
        HttpSession session = request.getSession();
        Object obj = session.getAttribute("user");
        if (obj != null)
            return true;
        // 没有登录且不是登录页面,转发到登录页面,并给出提示错误信息
        request.setAttribute("msg", "还没登录,请先登录!");
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,
                response);
        return false;
    }
    @Override
    public void afterCompletion(HttpServletRequest arg0,
                                HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        // TODO Auto-generated method stub
    }
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
                           Object arg2, ModelAndView arg3) throws Exception {
        // TODO Auto-generated method stub
    }
}

5)配置拦截器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描控制器类 -->
    <context:component-scan base-package="controller" />
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!-- 配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 配置拦截器作用的路径 -->
            <mvc:mapping path="/**" />
            <bean class="interceptor.LoginInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

6)创建视图 JSP 页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
${msg }
<form action="${pageContext.request.contextPath }/login" method="post">
    用户名:<input type="text" name="uname" /><br>
    密码:<input type="password" name="upwd" /><br>
    <input type="submit" value="登录" />
</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
当前用户:${user.uname }<br />
<a href="${pageContext.request.contextPath }/logout">退出</a>
</body>
</html>

7)发布并测试应用


第10章 文件的上传与下载

10.1 文件上传

10.1.1 单文件上传

1)创建应用并导入 JAR 包

commons-fileupload-1.2.2.jar
commons-io-2.4.jar
commons-lang-2.6.jar
commons-logging-1.2.jar
jstl.jar
standard.jar

2)创建 web.xml 文件

3)创建文件选择页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/onefile"
      method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="myOneFile"><br>
    文件描述:<input type="text" name="description"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

4)创建 POJO 类

package pojo;
import org.springframework.web.multipart.MultipartFile;
public class FileDomain {
    private String description;
    private MultipartFile myOneFile;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public MultipartFile getMyOneFile() {
        return myOneFile;
    }

    public void setMyOneFile(MultipartFile myOneFile) {
        this.myOneFile = myOneFile;
    }
}

5)创建 Controller 类

package controller;
import java.io.File;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.FileDomain;
@Controller
public class FileUploadController {
    // 得到一个用来记录日志的对象,这样在打印信息时能够标记打印的是哪个类的信息
    private static final Log logger = LogFactory.getLog(FileUploadController.class);
    /**
     * 单文件上传
     */
    @RequestMapping("/onefile")
    public String oneFileUpload(@ModelAttribute FileDomain fileDomain,
                                HttpServletRequest request) {
        /**
         * 文件上传到服务器的位置“/uploadfiles”,该位置是指
         * springMVCDemo07\\out\\artifacts\\springMVCDemo07_war_exploded\\uploadfiles
         */
        String realpath = request.getServletContext()
                .getRealPath("uploadfiles");
        String fileName = fileDomain.getMyOneFile().getOriginalFilename();
        File targetFile = new File(realpath, fileName);
        if (!targetFile.exists()) {
            targetFile.mkdirs();
        }
        // 上传
        try {
            fileDomain.getMyOneFile().transferTo(targetFile);
            logger.info("成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "showOne";
    }
}

6)创建 Spring MVC 的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 使用扫描机制扫描包 -->
    <context:component-scan base-package="controller" />
    <!-- 完成视图的对应 -->
    <!-- 对转向页面的路径解析。prefix:前缀, suffix:后缀 -->
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!-- 配置MultipartResolver,用于上传文件,使用spring的CommonsMultipartResolver -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="5000000" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>
</beans>

7)创建成功显示页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
${fileDomain.description }
<br>
<!-- fileDomain.getMyFile().getOriginalFilename()-->
${fileDomain.myOneFile.originalFilename }
</body>
</html>

8)测试文件上传

10.1.2 多文件上传

1)创建多文件选择页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/multifiles"
      method="post" enctype="multipart/form-data">
    选择文件1:<input type="file" name="myMultiFile"><br>
    文件描述1:<input type="text" name="description"><br />
    选择文件2:<input type="file" name="myMultiFile"><br>
    文件描述2:<input type="text" name="description"><br />
    选择文件3:<input type="file" name="myMultiFile"><br>
    文件描述3:<input type="text" name="description"><br />
    <input type="submit" value="提交">
</form>
</body>
</html>

2)创建 POJO 类

package pojo;
import java.util.List;
import org.springframework.web.multipart.MultipartFile;
public class MultiFileDomain {
    private List<String> description;
    private List<MultipartFile> myMultiFile;

    public List<String> getDescription() {
        return description;
    }

    public void setDescription(List<String> description) {
        this.description = description;
    }

    public List<MultipartFile> getMyMultiFile() {
        return myMultiFile;
    }

    public void setMyMultiFile(List<MultipartFile> myMultiFile) {
        this.myMultiFile = myMultiFile;
    }
}

3)添加多文件上传处理方法

 /**
     * 多文件上传
     */
    @RequestMapping("/multifiles")
    public String multiFileUpload(@ModelAttribute MultiFileDomain multiFileDomain,
                                  HttpServletRequest request) {
        String realpath = request.getServletContext().
                getRealPath("uploadfiles");
        File targetDir = new File(realpath);
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }
        List<MultipartFile> files = multiFileDomain.getMyMultiFile();
        for (int i = 0; i < files.size(); i++) {
            MultipartFile file = files.get(i);
            String fileName = file.getOriginalFilename();
            File targetFile = new File(realpath, fileName);
            // 上传
            try {
                file.transferTo(targetFile);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        logger.info("成功");

        return "showMulti";
    }

4)创建成功显示页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<table>
    <tr>
        <td>详情</td>
        <td>文件名</td>
    </tr>
    <!-- 同时取两个数组的元素 -->
    <c:forEach items="${multiFileDomain.description}" var="description"
               varStatus="loop">
        <tr>
            <td>${description}</td>
            <td>${multiFileDomain.myMultiFile[loop.count-1].originalFilename}</td>
        </tr>
    </c:forEach>
    <!-- fileDomain.getMyfile().getOriginalFilename() -->
</table>
</body>
</html>

5)测试文件上传

10.2 文件下载

10.2.1 文件下载的实现方法

  1. 通过超链接实现下载。
  2. 利用程序编码实现下载。
  1. Web 服务器需要告诉浏览器其所输出内容的类型不是普通文本文件或 HTML 文件,而是一个要保存到本地的下载文件,这需要设置 Content-Type 的值为 application/x-msdownload。
  2. Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。
response.setHeader("Content-Type", "application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;filename="+filename);

10.2.2 文件下载的过程

1)编写控制器类

package controller;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class FileDownController {
    // 得到一个用来记录日志的对象,在打印时标记打印的是哪个类的信息
    private static final Log logger = LogFactory
            .getLog(FileDownController.class);
    /**
     * 显示要下载的文件
     */
    @RequestMapping("showDownFiles")
    public String show(HttpServletRequest request, Model model) {
        /** 从 springMVCDemo07\\out\\artifacts\\springMVCDemo07_war_exploded\\uploadfiles
         * 下载
         */
        String realpath = request.getServletContext()
                .getRealPath("uploadfiles");
        File dir = new File(realpath);
        File files[] = dir.listFiles();
        // 获取该目录下的所有文件名
        ArrayList<String> fileName = new ArrayList<String>();
        for (int i = 0; i < files.length; i++) {
            fileName.add(files[i].getName());
        }
        model.addAttribute("files", fileName);
        return "showDownFiles";
    }
    /**
     * 执行下载
     */
    @RequestMapping("down")
    public String down(@RequestParam String filename,
                       HttpServletRequest request, HttpServletResponse response) {
        String aFilePath = null; // 要下载的文件路径
        FileInputStream in = null; // 输入流
        ServletOutputStream out = null; // 输出流
        try {
            // 从workspace\.metadata\.plugins\org.eclipse.wst.server.core\
            // tmp0\wtpwebapps下载
            aFilePath = request.getServletContext().getRealPath("uploadfiles");
            // 设置下载文件使用的报头
            response.setHeader("Content-Type", "application/x-msdownload");
            response.setHeader("Content-Disposition", "attachment; filename="
                    + toUTF8String(filename));
            // 读入文件
            in = new FileInputStream(aFilePath + "\\" + filename);
            // 得到响应对象的输出流,用于向客户端输出二进制数据
            out = response.getOutputStream();
            out.flush();
            int aRead = 0;
            byte b[] = new byte[1024];
            while ((aRead = in.read(b)) != -1 & in != null) {
                out.write(b, 0, aRead);
            }
            out.flush();
            in.close();
            out.close();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        logger.info("下载成功");
        return null;
    }
    /**
     * 下载保存时中文文件名的字符编码转换方法
     */
    public String toUTF8String(String str) {
        StringBuffer sb = new StringBuffer();
        int len = str.length();
        for (int i = 0; i < len; i++) {
            // 取出字符中的每个字符
            char c = str.charAt(i);
            // Unicode码值为0~255时,不做处理
            if (c >= 0 && c <= 255) {
                sb.append(c);
            } else { // 转换 UTF-8 编码
                byte b[];
                try {
                    b = Character.toString(c).getBytes("UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    b = null;
                }
                // 转换为%HH的字符串形式
                for (int j = 0; j < b.length; j++) {
                    int k = b[j];
                    if (k < 0) {
                        k &= 255;
                    }
                    sb.append("%" + Integer.toHexString(k).toUpperCase());
                }
            }
        }
        return sb.toString();
    }
}

2)创建文件列表页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<table>
    <tr>
        <td>被下载的文件名</td>
    </tr>
    <!--遍历 model中的 files-->
    <c:forEach items="${files}" var="filename">
        <tr>
            <td>
                <a href="${pageContext.request.contextPath }/down?filename=${filename}">${filename}</a>
            </td>
        </tr>
    </c:forEach>
</table>
</body>
</html>

3)测试下载功能