SpringMVC学习笔记

一、SpringMVC简介

SpringMVC是Spring提供的一个基于MVC设计模式的轻量级Web开发框架,本质上相当于Servlet。SpringMVC的角色划分清晰,分工明细。它本身就是Spring的一部分,可以说与Spring框架无缝集成。是当今业界最主流的 Web 开发框架。

用于软件开发的MVC架构
MVC设计模式一般指MVC框架,是一种三层架构,其中M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。MVC的目的是将M模型层的代码分离。

MVC三层架构的分工详细:
M(Model)模型层用于数据模型的封装,即客户请求的数据。
V(View)用户真正看到的前端页面,用于发送请求,展示请求回来的数据。
C(Controller)用于处理前端发送的客户请求,并将请求的结果返回。是视图层与模型层之
间的中间件。

二、Spring MVC的项目构建

项目环境

  • Java运行环境 JDK1.8
  • 服务器 Tomcat
  • 项目构建工具 Maven

MVC分层

  • M Model 数据访问层
  • V View 视图层
  • C Controller 控制层

项目依赖

<!--测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
<!--日志-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
</dependency>
<!--J2EE-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<!--mysql驱动包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.35</version>
</dependency>
<!--springframework-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.github.stefanbirkner</groupId>
    <artifactId>system-rules</artifactId>
    <version>1.16.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>
<!--其他需要的包-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

1.编写web.xml,注册DispatcherServlet。
Spring MVC 是基于 Servlet 的,DispatcherServlet 是整个 Spring MVC 框架的核心,主要负责截获请求并将其分派给相应的处理器处理。所以配置 Spring MVC,首先要定义 DispatcherServlet。跟所有 Servlet 一样,用户必须在 web.xml 中进行配置。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--1.注册DispatcherServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--关联一个springmvc的配置文件:【serlvet-name】 -servlet.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别
		值为1表示容器在启动时立即加载servlet
		-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--/ 匹配所有的请求: (不包括.jsp)-->
    <!--/* 匹配所有的请求: (包括.jsp)-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

2.编写springmvc配置文件。

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
    <context:component-scan base-package="com.kuang.controller"/>
    <!--让Spring MVC不处理静态资源 .css .js .html .mp3 .mp4-->
    <mvc:default-servlet-handler/>
    <!--
    支持MVC注解驱动
        在spring中一般采用@RequestMapping注解来完成映射关系
        要想使@RequestMapping注解生效
        必须向上下文中注册DefaultAnnotationHandlerMapping
        和一个AnnotationMethodHandlerAdapter实例
        这两个实例分别在类级别和方法级别处理
        而annotation-driven配置帮助我们自动完成上述两个实例的注入
    -->
    <mvc:annotation-driven/>

    <!--视图解析器
            要返回/WEB-INF/jsp包下的test.jsp页面
            只需要输入文件名即可,视图解析器会给我们添加它的前缀与后缀名
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

完成以上两个操作基本上我们就离开了通过编写Servlet来处理前端请求与响应的阶段,而是采用Controller控制器来处理。

3.创建控制器Controller

@Controller
public class HelloController {
    @RequestMapping("/hello")//访问地址:http://localhost:8080/hello
    public String hello(Model model){
        model.addAttribute("msg","数据集");//Model是一个数据模型,它用于封装数据返回给前端页面,这里的model作用域就相当于request。
        return "hello";//返回 /WEB-INF/jsp目录下的hello.jsp页面
    }
}

通过Contoller我们可以在一个方法上添加@RequestMapper("…")定义为一个访问地址,使用Model封装响应数据。且方法返回的字符串,经过视图解析器,如果有对应的视图就会返回。

三、SpringMVC的执行流程(视图解析器的作用)

本章主要了解Spring MVC 框架的整体请求流程和使用到的 API 类。
Spring MVC 框架是高度可配置的,包含多种视图技术,例如 JSP、FreeMarker、Tiles、iText 和 POI。Spring MVC 框架并不关心使用的视图技术,也不会强迫开发者只使用 JSP。
在这里插入图片描述SpringMVC 的执行流程如下。
1、用户发起一个 HTTP request 请求,该请求会被提交到 DispatcherServlet(前端控制器);
2、由 DispatcherServlet 请求一个或多个 HandlerMapping(处理器映射器),并返回一个执行链(HandlerExecutionChain)。
3、DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
4、HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(常称为 Controller);
5、Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC的底层对象,包括 Model 数据模型和 View 视图信息);
6、HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
7、DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
8、ViewResolver 根据 View 信息匹配到相应的视图结果,并返回给 DispatcherServlet;
9、DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
10、视图负责将结果显示到浏览器(客户端)。

SpringMVC执行流程中各组件使用的接口及作用

元件接口作用
前端控制器DispatcherServlet控制整个流程的执行,对各个组件进行统一调度
处理器映射器HandlerMapping根据请求的 URL 路径,通过注解或者 XML 配置,寻找匹配的处理器(Handler)信息。
处理器适配器HandlerAdapter根据映射器找到的处理器(Handler)信息,按照特定规则执行相关的处理器(Handler)。
处理器Handler执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装至 ModelAndView 对象中。
视图解析器View Resolver通过 ModelAndView 对象中的 View 信息将逻辑视图名解析成真正的视图 View(如通过一个 JSP 路径返回一个真正的 JSP 页面)。
视图View展示给前端用户的最终视图

以上组件中,需要开发人员进行开发的是处理器(Handler,常称Controller)和视图(View)。通俗的说,要开发处理该请求的具体代码逻辑,以及最终展示给用户的界面。

四、使用@Controller与@RequestMapping注解配置Handler

Spring 2.5 版本新增了 Spring MVC 注解功能,用于替换传统的基于 XML 的 Spring MVC 配置。
使用基于注解的控制器具有以下 2 个优点:
1.在基于注解的控制器类中可以编写多个处理方法,进而可以处理多个请求(动作),这就允许将相关的操作编写在同一个控制器类中,从而减少控制器类的数量,方便以后维护。
2.基于注解的控制器不需要在配置文件中部署映射,仅需要使用 @RequestMapping 注解一个方法进行请求处理即可。

@Controller注解
@Controller 注解用于声明某类的实例是一个控制器,标注与类上方。

package net.biancheng.controller;
import org.springframework.stereotype.Controller;
@Controller
public class IndexController {
    // 处理请求的方法
}

定义为@Controller的类需要注入Spring容器中才能生效

<!-- 使用扫描机制扫描控制器类,控制器类都在net.biancheng.controller包及其子包下 -->
<context:component-scan base-package="net.biancheng.controller" />

@RequestMapping注解
在基于注解的控制器类中可以为每个请求编写对应的处理方法。使用 @RequestMapping 注解将请求与处理方法一 一对应即可。@RequestMapping可用于类或方法上。用于类上,表示类中的所有响应请求的方法都以该地址作为父路径。

@Controller
public class IndexController {
    @RequestMapping(value = "/index/success" method=RequestMethod.GET, Params="username")
    public String success(@RequestParam String username) {        
        return "index";
    }
}

@RequestMapping的属性

属性作用
value@RequestMapping 注解的默认属性,因此如果只有 value 属性时,可以省略该属性名,如果有其它属性,则必须写上 value 属性名称。用于定义请求地址
path作用value相似。支持通配符匹配,如可定义@RequestMapping(path=“toUser/*”) 形式的请求。
name定义方法的注释信息
method指定请求的方式
params用于指定请求中规定的参数
hearder要求请求中必须要包含指定的"Referer请求头"
consumers用于指定处理请求的提交内容类型。如application/json,text/html
produces指定返回的内容类型或返回值的编码格式。如@RequestMapping(value = “toUser”,produces = “application/json,charset=utf-8”),表示返回 utf-8 编码。返回的内容类型必须是 request 请求头(Accept)中所包含的类型。

params属性详解
@RequestMapping(value = “toUser”,params = “type”)
params 属性用于指定请求中规定的参数,代码如下。

@RequestMapping(value = "toUser",params = "type")
public String toUser() {
    return "showUser";
}
//以上代码表示请求中必须包含 type 参数时才能执行该请求,请求为http://localhost:8080/toUser?type=xxx 才能够正常访问 toUser() 方法
@RequestMapping(value = "toUser",params = "type=1")
public String toUser() {
    
    return "showUser";
}
//请求中必须包含 type 参数,且 type 参数为 1 时才能够执行该请求。即 http://localhost:8080/toUser?type=1 才能够正常访问 toUser() 方法

在请求方法中还可以使用Servlet API

@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/login")
    public String login(HttpSession session,HttpServletRequest request) {
        session.setAttribute("skey", "session范围的值");
        session.setAttribute("rkey", "request范围的值");
        return "login";
    }
}

使用 org.springframework.ui.Model 类型返回数据。

//Model是一个包含Map的SpringMVC类型,在每次调用请求处理方法时SpringMVC都将创建一个Model对象
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/register")
    public String register(Model model) {
        /*在视图中可以使用EL表达式${success}取出model中的值*/
        model.addAttribute("success", "注册成功");
        return "register";
    }
}

五、接收请求参数的方式

Spring MVC Controller 接收请求参数的方式有很多种,有的适合 get 请求方式,有的适合 post 请求方式,有的两者都适合。主要有以下几种方式:

  • 通过实体 Bean 接收请求参数
  • 通过处理方法的形参接收请求参数
  • 通过 HttpServletRequest 接收请求参数
  • 通过 @PathVariable 接收 URL 中的请求参数
  • 通过 @RequestParam 接收请求参数
  • 通过 @ModelAttribute 接收请求参数

1、实体 Bean 接收请求参数适用于 get 和 post 提交请求方式。需要注意,Bean 的属性名称必须与请求参数名称相同。

@RequestMapping("/login")
public String login(User user, Model model) {
    if ("bianchengbang".equals(user.getName())
            && "123456".equals(user.getPwd())) {
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}

2、通过处理方法的形参接收请求参数。即形参名称与请求参数名称完全相同,该接收参数方式适用于 get 和 post 提交请求方式。

@RequestMapping("/login")
public String login(String name, String pwd, Model model) {
}
//请求地址为: http:localhost//8081?name=xxx&&pwd=xxx

3、通过 HttpServletRequest 接收请求参数,适用于 get 和 post 提交请求方式。

@RequestMapping("/login")
public String login(HttpServletRequest request, Model model) {
    String name = request.getParameter("name");
    String pwd = request.getParameter("pwd");
    if ("bianchengbang".equals(name)
            && "123456".equals(pwd)) {
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}

4、通过@PathVariable接收URL中的请求参数。(Restful风格的请求)

@RequestMapping("/login/{name}/{pwd}")
public String login(@PathVariable String name, @PathVariable String pwd, Model model) {
    if ("bianchengbang".equals(name)
            && "123456".equals(pwd)) {
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}
//访问地址为:http://localhost:8080/login/xiaoming/123456

5、通过@RequestParam接收请求参数
在方法入参处使用 @RequestParam 注解指定其对应的请求参数。@RequestParam 有以下三个参数:
value:参数名
required:是否必须,默认为 true,表示请求中必须包含对应的参数名,若不存在将抛出异常
defaultValue:参数默认值
通过 @RequestParam 接收请求参数适用于 get 和 post 提交请求方式,示例代码如下。

@RequestMapping("/login")
public String login(@RequestParam String name, @RequestParam String pwd, Model model) {
    if ("bianchengbang".equals(name)
            && "123456".equals(pwd)) {
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}
//该方式与“通过处理方法的形参接收请求参数”部分的区别如下:当请求参数与接收参数名不一致时,“通过处理方法的形参接收请求参数”不会报 404 错误,而“通过 @RequestParam 接收请求参数”会报 404 错误。

6、通过@ModelAttribute接收请求参数
@ModelAttribute 注解用于将多个请求参数封装到一个实体对象中,从而简化数据绑定流程。适用于 get 和 post 提交请求方式。
@ModelAttribute会自动暴露为模型数据,在视图页面展示时使用。二通过实体 Bean 接收请求参数”中只是将多个请求参数封装到一个实体对象,并不能暴露为模型数据,需要使用 model.addAttribute 语句才能暴露为模型数据。
,示例代码如下。
纯文本复制

@RequestMapping("/login")
public String login(@ModelAttribute("user") User user, Model model) {
   
    if ("bianchengbang".equals(name)
            && "123456".equals(pwd)) {
       
        model.addAttribute("message", "登录成功");
        return "main"; // 登录成功,跳转到 main.jsp
    } else {
        model.addAttribute("message", "用户名或密码错误");
        return "login";
    }
}

六、重定向与转发

springmvc与使用Servlet API的重定向与请求转发方式

    //使用Servlet API进行请求与转发,不经过视图解析器
    @RequestMapping("/forward")
    public String hello2(HttpServletRequest req) throws ServletException, IOException {
        req.setAttribute("msg","请求转发数据");
//        req.getRequestDispatcher("/WEB-INF/jsp/forward.jsp").forward(req,resp);//使用ServletAPI 要设置方法返回值为void,即不经过视图解析器
        return "forward:/WEB-INF/jsp/forward.jsp";//SpringMVC请求转发的方式,同以上代码作用相同
    }

    @RequestMapping("/redirect")
    public String hello3(HttpServletResponse resp,Model model) throws IOException {
//        resp.sendRedirect("/index.jsp");//使用ServletAPI进行重定向 注意:重定向不能访问WEB-INF目录下的文件
        return "redirect:/index.jsp";//可以是自定义的另一个请求地址
    }
    //带参数的重定向
    @RequestMapping(value = "/switchuser")
    public ModelAndView switchUser(User user, RedirectAttributes attr){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("redirect:/user/login");
        modelAndView.addObject("userName",user.getUserName());
        modelAndView.addObject("password",user.getPassword());
//        System.out.println(user);
//        attr.addAttribute();
//        attr.addAttribute("password",user.getPassword());
//        return "redirect:/user/login";
        return modelAndView;
    }

七、@ModelAttribute注解

@ModelAttribute是Spring MVC中非常重要的注解,它用于将请求参数绑定到 Model 对象。在 Controller 中使用 @ModelAttribute 时,有以下几种应用情况。

  • 应用在方法上
  • 应用在方法的参数上
  • 应用在方法上,并且方法也使用了 @RequestMapping(不推荐使用)

应用在方法上
1.应用在无返回值的方法

@Controller
public class HelloController {
    // 方法无返回值
    @ModelAttribute
    public void myModel(@RequestParam(required = false) String message, Model model) {
        model.addAttribute("message", message);
    }
    
    @RequestMapping(value = "/target")
    public String model() {
        return "target";
    }
}

target.jsp页面

<h2 style="color: yellowgreen">请求参数信息:${message}</h2>

开启Web服务后,访问http://localhost:8081/target?message=小明页面结果为
在这里插入图片描述
2.在有返回值的方法上
以上案例的myModel方法修改为

@ModelAttribute("message")
public String myModel(@RequestParam(required = false) String message, Model model) {
    return message;
}

最终的返回结果与以上案例相同
在这里插入图片描述

应用在方法的参数上
@ModelAttribute 注解在方法的参数上,调用方法时,模型的值会被注入。注意,@Model修饰String或bean等拥有无参构造方法的引用数据类型参数时是非必需的,而修饰int等基本数据类型参数是必需的。

@Controller
public class HelloController {

    @RequestMapping("/target")
    public String toTarget(@ModelAttribute("user") User u){//model.addAttribute("user",u)
        return "target";
    }
}

target.jsp视图

<h2 style="color: yellowgreen">请求参数信息:${user.name}${user.age}</h2>

用户访问http://localhost:8081/target?name=小明&age=1,返回结果如下。@ModelAttribute的参数允许没有,即访问http://localhost:8081/target也可以。
在这里插入图片描述

扩展:Model和ModelView的区别
Model:每次请求中都存在的默认参数,利用其 addAttribute() 方法即可将服务器的值传递到客户端页面中。
ModelAndView:包含 model 和 view 两部分,使用时需要自己实例化,利用 ModelMap 来传值,也可以设置 view 的名称。

八、类型转换器

Spring MVC 框架的 Formatter 与 Converter<S, T> 一样,也是一个可以将一种数据类型转换成另一种数据类型的接口。框架内置了许多的类型转换器,但有时需要编写具有特定功能的类型转换器。

自定义类型装换器
SpringMVC中使用实现Converter<S,T>接口的来自定义类型转换器,下面是定义将String字符串转换为User对象的转换器StringToUserConverter 。

public class StringToUserConverter implements Converter<String, User> {
    public User convert(String source) {
        User user = new User();
        String[] stringvalues = source.split(",");
        if (stringvalues.length == 2) {
            // 为user实例赋值
            user.setName(stringvalues[0]);
            user.setAge(Integer.parseInt(stringvalues[1]));
            return user;
        } else {
            throw new IllegalArgumentException(String.format("类型转换失败, 需要格式'编程帮, 18,1.85',但格式是[% s ] ", source));
        }
    }
}

要使自定义的转换器生效需要在spring-mvc文件中进行配置

<!--JSON乱码问题配置-->
<mvc:annotation-driven conversion-service="conversionService"/>

<!--注册类型转换器UserConverter -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
 <property name="converters">
     <list>
         <bean class="com.example.converter.StringToUserConverter"/>
     </list>
 </property>
</bean>

创建Controller及taret.jsp页面

@Controller
public class HelloController {

    @RequestMapping("/target")
    public String toTarget(@ModelAttribute("user") User u){//model.addAttribute("user",u)
        return "target";
    }
}
<h2 style="color: yellowgreen">请求参数信息:${user.name}${user.age}</h2>

访问地址http://localhost:8081/target?user=小明,20最终展示的结果为
在这里插入图片描述

九、数据格式化Formatter

Spring MVC 框架的 Formatter 与 Converter<S, T> 一样,都是一个可以将一种数据类型转换成另一种数据类型的接口。不同的是,Formatter 的源类型必须是 String 类型,而 Converter 的源类型可以是任意数据类型。Formatter 更适合 Web 层,因为在 Web 应用中由 HTTP 发送的请求数据到控制器中都是以 String 类型获取,而 Converter 可以在任意层中。

实例示例
下面是一个User的实体类,现在希望前端输入的birth字段值格式是yyyy-MM-dd的形式。下面定义格式转换器MyFormatter。
(1)定义实现了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只能对字符串转换
    }
}

(2)在Spring中配置转换器

<!--JSON乱码问题配置-->
<mvc:annotation-driven conversion-service="conversionServiceFormatter">
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="conversionServiceFormatter"
      class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
        <set>
            <bean class="com.example.converter.MyFormatter"/>
        </set>
    </property>
</bean>

(3)定义请求接口及目标页面

@RequestMapping("/target")
public String toTarget(@ModelAttribute("user") User u){//model.addAttribute("user",u)
    return "target";
}
<h2 style="color: yellowgreen">请求参数信息:${user.name}${user.age}${user.birth}</h2>

(4)访问地址http://localhost:8081/target?name=%E5%B0%8F%E6%98%8E&age=100&birth=2000-10-3的结果
在这里插入图片描述

十、Spring MVC表单标签库

Spring 也有自己的一套表单标签库,通过 Spring 表单标签,可以很容易地将模型数据中的命令对象绑定到 HTML 表单元素中。

十一、JSON数据交互

Spring MVC 在数据绑定的过程中需要对传递数据的格式和类型进行转换,它既可以转换 String 等类型的数据,也可以转换 JSON 等其他类型的数据。
为实现浏览器与控制器类之间的 JSON 数据交互,Spring MVC 提供了 MappingJackson2HttpMessageConverter 实现类默认处理 JSON 格式请求响应。该实现类利用 Jackson 开源包读写 JSON 数据,将 Java 对象转换为 JSON 对象和 XML 文档,同时也可以将 JSON 对象和 XML 文档转换为 Java 对象。

在使用注解开发时需要用到两个重要的 JSON 格式转换注解,分别是 @RequestBody 和 @ResponseBody。
@RequestBody:用于将请求体中的数据绑定到方法的形参中,该注解应用在方法的形参上。
@ResponseBody:用于直接返回 return 对象,该注解应用在方法上。

解决JSON乱码问题
导入jackson依赖

<!--JACKSON-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0.pr3</version>
</dependency>

配置JSON传输数据的编码格式为UTF-8

<!--5.JSON乱码问题配置-->
<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

十二、拦截器

Spring MVC提供的Interceptor拦截器机制可以在处理用户请求之前和之后执行一些行为,完成对请求的预处理和后处理,例如检测用户权限实现访问控制,或者将请求的信息记录到日志中,即平时所说的“权限检测”及“日志记录”。

要在SpringMVC中使用拦截器拦截指定的请求,需要进行定义拦截器和配置两个步骤。
定义拦截器的方式
实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(例如 HandlerInterceptorAdapter)
通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类

示例:通过实现HandlerInterceptor接口定义拦截器

public class MyInterceptor implements HandlerInterceptor {
    
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return false;
    }
    
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

preHandle( ):该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
postHandle( ):该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
afterCompletion( ):该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。

配置拦截器

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

<bean>:该元素是 <mvc:interceptors>的子元素,用于定义全局拦截器,即拦截所有的请求。
<mvc:interceptor>:该元素用于定义指定路径的拦截器。
<mvc:mapping>:该元素是 <mvc:interceptor> 的子元素,用于配置拦截器作用的路径,该路径在其属性 path 中定义。path 的属性值为/**时,表示拦截所有路径,值为/gotoTest时,表示拦截所有以/gotoTest结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置。

拦截器路径匹配规则

十三、数据校验

一般情况下,用户的输入是随意的,为了保证数据的合法性,数据验证是所有 Web 应用必须处理的问题。

数据验证分为客户端验证和服务器端验证,客户端验证主要是过滤正常用户的误操作,通过 JavaScript 代码完成。服务器端验证是整个应用阻止非法数据的最后防线,通过在应用中编程实现。

JSR303校验
JSR 303 是 Java 为 Bean 数据合法性校验所提供的标准框架。JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。
JSR 303 不需要编写验证器,它定义了一套可标注在成员变量、属性方法上的校验注解,如下表所示。

名称说明
@Null被标注的元素必须为 null
@NotNull被标注的元素必须不为 null
@AssertTrue被标注的元素必须为 true
@AssertFalse被标注的元素必须为 false
@Min(value)被标注的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被标注的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMax(value)被标注的元素必须是一个数字,其值必须大于等于指定的最大值
@DecimalMin(value)被标注的元素必须是一个数字,其值必须小于等于指定的最小值
@size被标注的元素的大小必须在指定的范围内
@Digits(integer,fraction)被标注的元素必须是一个数字,其值必须在可接受的范围内;integer 指定整数精度,fraction 指定小数
@Past被标注的元素必须是一个过去的日期
@Future被标注的元素必须是一个将来的日期
@Pattern(value)被标注的元素必须符合指定的正则表达式

Spring MVC 支持 JSR 303 标准的校验框架,Spring 的 DataBinder 在进行数据绑定时,可同时调用校验框架来完成数据校验工作,非常简单方便。在 Spring MVC 中,可以直接通过注解驱动的方式来进行数据校验。

Spring 本身没有提供 JSR 303 的实现,Hibernate Validator 实现了 JSR 303,所以必须在项目中加入来自 Hibernate Validator 库的 jar 文件,下载地址为 http://hibernate.org/validator/。本节使用版本为 hibernate-validator-5.1.0.Final-dist.zip,复制其中的 3 个 jar 文件即可,Spring 将会自动加载并装配。
hibernate-validator-5.1.0.Final.jar
jboss-logging-3.1.0.CR2.jar
validation-api-1.0.0.GA.jar

<!-- 数据校验 -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.jboss.logging</groupId>
    <artifactId>jboss-logging</artifactId>
    <version>3.1.0.CR2</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.1.0.Final</version>
</dependency>

在实体类上加上校验条件

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
	
	@NotNull(message="id不能为空")
	private Long id;
	
    @Length(min = 2, max = 8, message = "用户名不能少于2位大于8位")
    private String name;

    @Max(value = 200,message = "age的最大值为200")
    private Integer age;

    private Date birth;

    @Email(regexp = "[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]", message = "邮箱格式不正确")
    private String email;
    
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

请求及处理

@RequestMapping("/validate")
@ResponseBody
public String validate(@Valid User user,BindingResult result){
    // 如果有异常信息
    if (result.hasErrors()) {
        // 获取异常信息对象
        List<ObjectError> errors = result.getAllErrors();
        // 将异常信息输出
        for (ObjectError error : errors) {
			System.out.println("错误:" + error.getDefaultMessage());
        }
    }
    return "验证通过";
}

validate通过BindingResult对象捕获非法的验证,可以在方法中自定义处理。这里将错误信息打印了到控制台。
当我们发送不符合Bean验证条件的请求http://localhost:8081/validate?name=fgjklg&age=500时,会在控制台输出如下错误信息:
在这里插入图片描述

十四、异常处理

在 Spring MVC 应用的开发中,不管是操作底层数据库,还是业务层或控制层,都会不可避免地遇到各种可预知的、不可预知的异常。我们需要捕捉处理异常,才能保证程序不被终止。

Spring MVC 有以下 3 种处理异常的方式:
异常处理方式一:使用 @ExceptionHandler注解定义单个Controller类中的局部异常处理

@Controller
public class HelloController {
    @RequestMapping("/toPage")
    public String toPage(){
        int i = 10/0;//出现运行时异常算术异常
        return "page";
    }

    @ExceptionHandler({ArithmeticException.class})
    public String arithmeticException(Exception e){
        System.out.println(e);
        return "arithmeticError";
    }

    @ExceptionHandler({Exception.class})
    public String exception(){
        return "error";
    }
}

当用户请求http://localhost:8081/toPage地址是时,会发生算术异常,根据优先原则最终会跳转到继承关系最近的arithmeticError页面。

异常处理方式二:通过实现HandlerExceptionResolver接口自定义全局异常处理器

@Component
public class MyExceptionHandler implements HandlerExceptionResolver {

    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        Map<String, Object> model = new HashMap<String, Object>();
        System.out.println(ex);
        if(ex instanceof ArithmeticException){
            return new ModelAndView("arithmeticError",model);
        }
        return new ModelAndView("error",model);
    }
}

注意:当我们同时定义了局部异常与全局异常处理时,会优先进行局部异常处理,且不会考虑继承关系的远近。

异常处理方式三:使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver定义全局异常处理。

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!--定义默认的异常处理页面-->
    <property name="defaultErrorView" value="error"/>
    <!--定义异常的变量名-->
    <property name="exceptionAttribute" value="ex"/>
    <!--定义指定类型的异常处理,key为异常类名或全限定名,value为错误页面-->
    <property name="exceptionMappings">
        <props>
            <prop key="ArithmeticException">arithmeticError</prop>
        </props>
    </property>
</bean>

建议不要与HandlerExceptionResolver定义处理一起使用,以免混淆。

十五、RESTFUL风格

REST(Representational State Transfer)即表述性转移,是目前最流行的一种软件架构风格。它结构清晰、易于理解、有较好的扩展性。

Spring REST 风格可以简单理解为:使用 URL 表示资源时,每个资源都用一个独一无二的 URL 来表示,并使用 HTTP 方法表示操作,即准确描述服务器对资源的处理动作(GET、POST、PUT、DELETE),实现资源的增删改查。
GET:表示获取资源
POST:表示新建资源
PUT:表示更新资源
DELETE:表示删除资源

示例:
由于 HTTP 不支持 PUT 和 DELETE 请求,所以需要将 DELETE 和 PUT 请求转换成 POST 请求,在 web.xml 中配置过滤器 HiddenHttpMethodFilter。

<!-- HiddenHttpMethodFilter过滤器可以将POST请求转化为put请求和delete请求! -->
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

前端请求编辑方式

<%@ 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>REST风格</title>
</head>
<body>
    <h4>发送GET请求</h4>
    <a href=" user/1">GET</a>
    <h4>发送POST请求</h4>
    <form action="user/1" method="post">
        <input type="submit" value="POST" />
    </form>
    <!-- 发送PUT和DELETE请求时,需要添加一个隐藏域 -->
    <h4>发送PUT请求</h4>
    <form action=" user/1" method="post">
        <input type="hidden" name="_method" value="PUT" /> <input
            type="submit" value="PUT" />
    </form>
    <h4>发送DELETE请求</h4>
    <input type="hidden" name="_method" value="DELETE" />
    <form action=" user/1" method="post">
        <input type="hidden" name="_method" value="DELETE" /> <input
            type="submit" value="DELETE" />
    </form>
</body>
</html>

定义Controller,开启服务即可测试使用。

@Controller
public class UserController {
    @RequestMapping(value = "/user/{id}", method = {RequestMethod.GET,RequestMethod.POST})
    public String hello(@PathVariable Integer id) {
        System.out.println("GET or POST REQUEST:" + id);
        return "success";
    }
    @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
    public String helloDelete(@PathVariable Integer id) {
        System.out.println("DELETE REQUEST:" + id);
        return "success";
    }
    @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
    public String helloPUt(@PathVariable Integer id) {
        System.out.println("PUT REQUEST:" + id);
        return "success";
    }
}

注意:如果项目是运行在 Tomcat 8 及以上,会发现发出的 PUT 请求和 DELETE
请求可以被控制其接收到,但是返回页面时(forward)会报HTTP 405 的错误提示:“消息 JSP 只允许 GET、POST 或 HEAD。Jasper 还允许OPTIONS”。
解决方法:HTTP405报错:JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS

十六、文件上传

在 Spring MVC 中实现文件上传十分容易,它为文件上传提供了直接支持,即 MultpartiResolver 接口。MultipartResolver 用于处理上传请求,将上传请求包装成可以直接获取文件的数据,从而方便操作。

MultpartiResolver 接口定义以下方法:

名称作用
byte[] getBytes()以字节数组的形式返回文件的内容
String getContentType()返回文件的内容类型
InputStream getInputStream()返回一个InputStream,从中读取文件的内容
String getName()返回请求参数的名称
String getOriginalFillename()返回客户端提交的原始文件名称
long getSize()返回文件的大小,单位为字节
boolean isEmpty()判断被上传文件是否为空
void transferTo(File destination)将上传文件保存到目标目录下

MultpartiResolver 接口有以下两个实现类:
StandardServletMultipartResolver:使用了 Servlet 3.0 标准的上传方式。
CommonsMultipartResolver:使用了 Apache 的 commons-fileupload 来完成具体的上传操作。

示例:使用CommonsMultipartResolver来完成文件上传
环境搭建
使用 Apache Commons FileUpload 组件完成文件上传,需要导入 commons-io-2.4.jar 和 commons-fileupload-1.2.2.jar 两个 jar 文件(可在 Apache 官网下载)。

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.2.2</version>
</dependency>

在Spring配置文件中使用 CommonsMultipartReslover 配置 MultipartResolver 解析器

<!-- 配置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>

单文件上传
文件上传表单页面fileupload.jsp。需要注意的是在进行文件上传时要设置表单enctype属性值为multipart/form-data,同时将表单的提交方式设置为 post。

<%@ 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 }/fileupload"
        method="post" enctype="multipart/form-data">
        <%--file输入框name属性必须与FileDomain中的file属性同名--%>
        选择文件:<input type="file" name="file"><br> 
        文件描述:<input type="text" name="description"><br> 
        <input type="submit" value="提交">
    </form>
</body>
</html>

创建实体类FileDomain用于封装上传的文件信息

@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileDomain {
    private String description;
    private MultipartFile file;
}

创建用于显示文件上传数据的页面showFile.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>文件上传</title>
</head>
<body>
文件描述:${fileDomain.description }
<br>
<!-- fileDomain.getMyFile().getOriginalFilename()-->
文件名称:${fileDomain.file.originalFilename }
<br>	
上传地址:${uploadPath}
</body>
</html>

编写Controller请求处理器

@Controller
public class FileController {
    /**
     * 单文件上传
     */
    @RequestMapping("/fileupload")
    public String oneFileUpload(@ModelAttribute("fileDomain") FileDomain fileDomain, HttpServletRequest request,Model model) {
        String realpath = request.getServletContext().getRealPath("/upload");
        System.out.println("上传地址:" + realpath);
        String fileName = fileDomain.getFile().getOriginalFilename();
        File targetFile = new File(realpath, fileName);
        if (!targetFile.exists()) {
            targetFile.mkdirs();
        }
        // 上传
        try {
            fileDomain.getFile().transferTo(targetFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
        model.addAttribute("uploadPath",realpath);
        return "showFile";
    }
}

演示图就不放了,可以自己去试一下。

扩展:表单的 enctype 属性指定的是表单数据的编码方式,该属性有以下 3 个值。
application/x-www-form-urlencoded:这是默认的编码方式,它只处理表单域里的 value 属性值。
multipart/form-data:该编码方式以二进制流的方式来处理表单数据,并将文件域指定文件的内容封装到请求参数里。
text/plain:该编码方式只有当表单的 action 属性为“mailto:”URL 的形式时才使用,主要适用于直接通过表单发送邮件的方式。

多文件上传
创建多文件上传数据包装类MultipeFileDomain.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MultiFileDomain {
    private List<String> description;
    private List<MultipartFile> file;
}

编写Controller请求处理器

@Controller
public class FileController {
    /**
     * 多文件上传
     */
    @RequestMapping("/multiFileUpload")
    public String multiFileUpload(@ModelAttribute("multiFileDomain") MultiFileDomain multiFileDomain, HttpServletRequest request,Model model) {
        String realpath = request.getServletContext().getRealPath("upload");
        File targetDir = new File(realpath);
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }
        List<MultipartFile> files = multiFileDomain.getFile();
        System.out.println("files"+files);
        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();
            }
        }
        model.addAttribute("uploadPath",realpath);
        return "showMultiple";
    }
}

多文件上传页面multiFile.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<%@ taglib prefix="fm" uri="http://www.springframework.org/tags/form"%>
<!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 }/multiFileUpload"
      method="post" enctype="multipart/form-data">
    选择文件1:<input type="file" name="file"><br>
    文件描述1:<input type="text" name="description"><br>
    选择文件2:<input type="file" name="file"><br>
    文件描述2:<input type="text" name="description"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

请求信息显示页面showmultiFile.jsp

<%@ 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>多文件上传显示</title>
</head>
<body>
<table border="1px">
    <tr>
        <td>详情</td>
        <td>文件名</td>
    </tr>
    <!-- 同时取两个数组的元素 -->
    <c:forEach items="${multiFileDomain.description}" var="description"
               varStatus="loop">
        <tr>
            <td>${description}</td>
            <td>${multiFileDomain.file[loop.count-1].originalFilename}</td>
        </tr>
    </c:forEach>
</table>
上传地址:${uploadPath}
</body>
</html>

十七、文件下载

顾名思义,文件下载就是将服务器中的文件下载到本地,它有以下两种实现方法
1、通过超链接实现下载:实现简单,但暴露了下载文件的真实位置,并且只能下载 Web 应用程序所在目录下的文件,WEB-INF 目录除外。
2、利用程序编码实现下载:增强安全访问控制,可以下载除 Web 应用程序所在目录以外的文件,也可以将文件保存到数据库中。

程序编码实现文件下载
利用程序编码实现下载需要设置以下两个报头:
(1)设置 Content-Type 的值为 application/x-msdownload,这是让WEB服务器告诉浏览器其所输出内容的类型不是普通文本文件或 HTML 文件,而是一个要保存到本地的下载文件。
(2)Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。
示例:

response.setHeader("Content-Type", "application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;filename="+filename);

程序编码文件下载可分为两个步骤:
(1)在客户端使用一个文件下载超链接,链接指向后台下载文件的方法以及文件名。
(2)在控制器类中,提供文件下载方法进行下载。

下面是程序编码下载的具体实现
1、创建FileDownController

@Controller
public class FileDownController {
    // 得到一个用来记录日志的对象,在打印时标记打印的是哪个类的信息
    private static final Log logger = LogFactory
            .getLog(FileDownController.class);
    /**
     * 显示要下载的文件
     */
    @RequestMapping("showDownFiles")
    public String show(HttpServletRequest request, Model model) {
        String realpath = request.getServletContext()
                .getRealPath("upload");
        File dir = new File(realpath);
        File files[] = dir.listFiles();
        // 获取该目录下的所有文件名
        List<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 {
            aFilePath = request.getServletContext().getRealPath("upload");
            // 设置下载文件使用的报头
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Type", "application/x-msdownload");
            response.setHeader("Content-Disposition", "attachment; filename="
                    + URLEncoder.encode(filename,"UTf-8"));

            // 读入文件
            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;
    }
}

2、showDownFile.jsp显示供下载的文件

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<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>

十八、处理静态资源的访问

DispacherServlet拦截路径为"/"时,它会将前端的请求包括静态资源的请求看成是一个处理器请求(即动态请求)。如何才能到访问Web中的静态资源,让Web服务器处理我们的静态请求。
查看解决方案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值