资讯专栏INFORMATION COLUMN

SpringMVC入门笔记

gekylin / 2272人阅读

摘要:简介注解用于修饰的方法,根据的的内容,通过适当的转换为客户端需要格式的数据并且写入到的数据区,从而不通过视图解析器直接将数据响应给客户端。并且这些解析器都实现了接口,在接口中有四个最为主要的接口方法。

SpringMVC 细节方面的东西很多,所以在这里做一篇简单的 SpringMVC 的笔记记录,方便以后查看。

Spring MVC是当前最优秀的MVC框架,自从Spring 2.5版本发布后,由于支持注解配置,易用性有了大幅度的提高。Spring 3.0更加完善,实现了对老牌的MVC框架Struts 2的超越,现在版本已经到了Spring5.x了。

一、工程创建 1.添加架包

创建Maven的web工程,添加依赖架包

Maven架包添加 spring-contextspring-webspring-webmvclog4j

2.web.xml配置

在web.xml中配置DispatcherServlet


  dispatcherServlet
  org.springframework.web.servlet.DispatcherServlet
  
    contextConfigLocation
    classpath:spring-mvc.xml
  
  1



  dispatcherServlet
  /

注意:这里配置的 / 拦截资源配置的是 /,拦截所有除其他 servlet 之外的资源访问,包括 jsp、静态网页、图片等等。与 /* 不一样,/* 一般配在拦截器里面,拦截所有资源访问。

3.创建SpringMVC的配置文件

上面配置 DispatcherServlet 里面用到了 contextConfigLocation 配置文件的地址,下面来创建配置文件。




    
    

    
    

    
    

    
    
        
        
    

    
    

二、@RequestMapping 注解

在对 SpringMVC 进行的配置的时候, 需要我们指定请求与处理方法之间的映射关系。 指定映射关系,就需要我们用上 @RequestMapping 注解。
@RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一,这个注解会将 HTTP 请求映射到控制器(Controller类)的处理方法上。

1.value和method属性

简单例子

@RequestMapping("rm")
@Controller
public class RequestMappingController {

    @RequestMapping(value = {"home", "/", ""}, method = RequestMethod.GET)
    public String goRMHome() {
        System.out.println("访问了 Test RequestMapping 首页");
        return "1-rm";
    }
}

最终访问路径是 .../rm/home,通过该方法返回视图名字和SpringMVC视图解析器加工,最终会转发请求到 .../WEB-INF/jsp/1-rm.jsp 页面。
如果没有类名上面的 @RequestMapping("rm"),则访问路径为 .../home
method 指定方法请求类型,取值有 GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE。
value 为数组字符串,指定访问路径与方法对应,指定的地址可以是URI。URI值可以是中:普通的具体值、包含某变量、包含正则表达式。

下面以包含某一变量举例:

@RequestMapping(value = "testPathVariable/{username}", method = RequestMethod.GET)
public String testPathVariable(@PathVariable(value = "username") String name) {
    //参数部分也可以直接写成 @PathVariable String username, 省略value, 保证形参名与上面 {} 内的名字一致
    //不建议省略
    System.out.println("访问了 Test PathVariable 方法 username: " + name);
    return "success";
}
2.consumes属性

指定处理请求的提交内容类型(Content-Type)

@RequestMapping(value = "testConsumes", method = RequestMethod.POST, consumes = "application/x-www-form-urlencoded")
public String testConsumes() {
    System.out.println("访问了 Test Consumes 方法");
    return "success";
}

如果请求里面的 Content-Type 对不上会报错

3.produces属性

指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回

其中 */*;q=0.8 表明可以接收任何类型的,权重系数0.8表明如果前面几种类型不能正常接收则使用该项进行自动分析。

@RequestMapping(value = "testProduces", method = RequestMethod.POST, produces = "text/html")
public String testProduces() {
    return "success";
}
4.params属性

指定request中必须包含某些参数值,才让该方法处理

JSP页面请求

用户名:
密 码:

Controller 里面对应的请求方法

@RequestMapping(value = "testParams", method = RequestMethod.POST, params = {"username!=Tom", "password"})
public String testParams() {
    return "success";
}

params = {"username!=Tom", "password"} 表示请求参数里面 username !=Tom 且有包含 password,二者有一个不满足则会报错

5.headers属性

指定 request 中必须包含某些指定的 header 值,才能让该方法处理请求

@RequestMapping(value = "testHeaders", method = RequestMethod.GET, headers = "Accept-Language=zh-CN,zh;q=0.9")
public String testHeaders() {
    return "success";
}

如果跟设定头里面对不上会报404错误

三、@RequestParam注解

请求

单参数 GET 请求方式
1.省略注解

表单元素的name名字和控制器里的方法的形参名一致,此时可以省略 @RequestParam 注解

@RequestMapping(value = "testGetOneParam", method = RequestMethod.GET)
public String testGetOneParam(String username) {
    System.out.println("访问了 单参数 Get 请求方法 username: " + username);
    return "success";
}
2.不省略注解

示例

@RequestMapping(value = "testPostOneParam", method = RequestMethod.POST)
public String testPostOneParam(@RequestParam String username) {
    System.out.println("username: " + name);
    return "success";
}

参数名字不一致时

@RequestMapping(value = "testPostOneParam", method = RequestMethod.POST)
public String testPostOneParam(@RequestParam(value = "username", required = false, defaultValue = "") String name) {
    System.out.println("username: " + name);
    return "success";
}

value 属性指定传过来的参数名,跟方法里的形参名字对应上
required 指定该参数是否是必须携带的
defaultValue 没有或者为 null 时,指定默认值

注:省略和不省略 @RequestParam 注解,最终SpringMVC内部都是使用 RequestParamMethodArgumentResolver 参数解析器进行参数解析的。如果省略 @RequestParam 注解或省略 @RequestParam 注解的 value 属性则最终则以形参的名字作为 keyHttpServletRequest 中取值。

四、@RequestHeader 和 @CookieValue 注解

@RequestHeader 注解:可以把 Request 请求 header 部分的值绑定到方法的参数上

@RequestMapping(value = "rh")
@Controller
public class RequestHeaderController {

    @RequestMapping(value = "testRHAccept", method = RequestMethod.GET)
    public String testRHAccept(@RequestHeader(value = "Accept") String accept) {
        System.out.println(accept);
        return "success";
    }

    @RequestMapping(value = "testRHAcceptEncoding", method = RequestMethod.GET)
    public String testRHAcceptEncoding(@RequestHeader(value = "Accept-Encoding") String acceptEncoding) {
        System.out.println(acceptEncoding);
        return "success";
    }
}

@CookieValue 注解:可以把Request header中关于cookie的值绑定到方法的参数上

@RequestMapping(value = "cv")
@Controller
public class CookieValueController {
    @RequestMapping(value = "testGetCookieValue", method = RequestMethod.GET)
    public String testGetCookieValue(@CookieValue(value = "JSESSIONID") String cookie) {
        System.out.println("获取到Cookie里面 JSESSIONID 的值 " + cookie);
        return "success";
    }
}
五、数据结果封装 ModelAndView & ModelMap & Map & Model

SpringMVC 为了方便数据封装和处理,提供了以下几种方案,最终会将封装到模型里面的数据全都通过 request.setAttribute(name, value) 添加request请求域中。

1.ModelAndView

使用 ModelAndView 类用来存储处理完后的结果数据,以及显示该数据的视图。从名字上看 ModelAndView 中的 Model 代表模型,View 代表视图。model ModelMap 的类型,而 ModelMap 又是 LinkedHashMap 的子类,view 包含了一些视图信息。

@RequestMapping(value = "testReturnModelAndView", method = RequestMethod.GET)
public ModelAndView testReturnModelAndView() {

    Student s1 = new Student(1, "Tom", 13, new Date());
    Student s2 = new Student(2, "Jerry", 14, new Date());

    List list = new ArrayList<>();
    list.add(s1); list.add(s2);

    HashMap map = new HashMap<>();
    map.put("s1", s1); map.put("s2", s2);

    ModelAndView mv = new ModelAndView();
    mv.addObject("s1", s1);
    mv.addObject("s2", s2);

    mv.addObject("list", list);
    mv.addObject("map", map);
    mv.setViewName("5-m&v-success");
    return mv;
}
2.ModelMap & Map & Model

最终也是将封装的数据和返回视图名字封装成 ModelAndView对象

@RequestMapping(value = "testMapParam", method = RequestMethod.GET)
public String testMapParam(Map paramMap) {
    ...
    paramMap.put("s1", s1);
    paramMap.put("s2", s2);

    paramMap.put("list", list);
    paramMap.put("map", map);
    return "5-m&v-success";
}

@RequestMapping(value = "testModelParam", method = RequestMethod.GET)
public String testModelParam(Model model) {
    ...
    model.addAttribute("s1", s1);
    model.addAttribute("s2", s2);

    model.addAttribute("list", list);
    model.addAttribute("map", map);
    return "5-m&v-success";
}

@RequestMapping(value = "testModelMapParam", method = RequestMethod.GET)
public String testModelMapParam(ModelMap modelMap) {
    ...
    modelMap.addAttribute("s1", s1);
    modelMap.addAttribute("s2", s2);

    modelMap.addAttribute("list",list);
    modelMap.addAttribute("map", map);
    return "5-m&v-success";
}
3.JSP页面提取数据
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>



    
        

单个数据封装

s1
姓名${s1.name}
年龄${s1.age}
生日${s1.birthday.toString()}
s2
姓名${s2.name}
年龄${s2.age}
生日${s2.birthday.toString()}

List数据封装

${status.count}
姓名${s.name}
年龄${s.age}
生日${s.birthday.toString()}

Map数据封装

${node.key}
姓名${node.value.name}
年龄${node.value.age}
生日${node.value.birthday.toString()}
六、@SessionAttributes

如果我们希望在多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个 @SessionAttributes,SpringMVC 将把模型中对应的属性暂存到 HttpSession 的域中。

使用方法:
@SessionAttributes(value={"xxx"}, types={xxxx.class})
value:是通过键来指定放入HttpSession 的域中的值;
types:是通过类型指定放入HttpSession 的域中的值;

@SessionAttributes(types=Student.class)
这个注解会将类中所有放入 Request 域中的 Student 对象同时放进 HttpSession 的域空间中。

可以添加多个属性
@SessionAttributes(value={“s1”, “s2”})
@SessionAttributes(types={User.class, Grade.class})

可以混合使用
@SessionAttributes(value={“s1”, “s2”},types={Grade.class})

示例

//@SessionAttributes(value = {"s1", "s2"})
@SessionAttributes(types = Student.class)
@RequestMapping(value = "sa")
@Controller
public class SessionAttributesController {

    @RequestMapping(value = "testSA", method = RequestMethod.GET)
    public String testSessionAttributes(Model model) {
        Student s1 = new Student(1, "Tom", 13, new Date());
        Student s2 = new Student(2, "Jerry", 13, new Date());

        model.addAttribute("s1", s1);
        model.addAttribute("s2", s2);
        return "6-sa-success";
    }
}

JSP 页面提取数据

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>


    request s1 : ${requestScope.get("s1")}

request s2 : ${requestScope.get("s2")}

session s1 : ${sessionScope.get("s1")}

session s2 : ${sessionScope.get("s2")}

七、@ModelAttribute

该注解平时使用的比较多,不仅可以写在方法上面也可以写在参数前面。

1.写在方法上面

在同一个控制器中,标注了@ModelAttribute 的方法实际上会在 @RequestMapping 注解方法之前被调用。

标注了@ModelAttribute 的方法能接受与@RequestMapping 标注相同的参数类型,只不过不能直接被映射到具体的请求上。

标注在方法上的 @ModelAttribute 说明方法一般是用于添加一个或多个属性到 model 上。

模拟请求

模拟请求
① 省略 value 属性值手动加入属性
@ModelAttribute
public void modelAttributeMethod1(ModelMap modelMap) {
    Person person = new Person("超哥哥 1 号", 12);
    modelMap.addAttribute("person1", person);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
    modelMap.forEach((key, value) -> {
        System.out.println(key + " = " + value);
        //person1 = Person{name="超哥哥 1 号", age=12}
    });
    return "success";
}

可以看出手动加入 model 里面属性成功,key 为自定义的字符串。

② 省略 value 属性值自动加入属性
@ModelAttribute
public Person modelAttributeMethod2() {
    return new Person("超哥哥 2 号", 12);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
    modelMap.forEach((key, value) -> {
        System.out.println(key + " = " + value);
        //person = Person{name="超哥哥 2 号", age=12}
    });
    return "success";
}

可以看出 @ModelAttribute 修饰的方法没有指定 value 属性时,让其自动加入的 key 是以添加类的类名首字母小写。

③ 指明 value 属性值自动加入属性
@ModelAttribute(value = "person3")
public Person modelAttributeMethod3() {
    return new Person("超哥哥 3 号", 13);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
    modelMap.forEach((key, value) -> {
        System.out.println(key + " = " + value);
        //person3 = Person{name="超哥哥 3 号", age=13}
    });
    return "success";
}

从上面可以看出 @ModelAttribute 修饰的方法有指定 value 属性时,让其自动加入的 key 就是自定的 value 属性的值。

2.写在参数前面

标注在方法参数前的 @ModelAttribute 说明了该方法参数的值将由 model 中取得,如果 model 中找不到,那么该参数会先被实例化,然后被添加到 model 中。在 model 中存在以后,将请求中所有名称匹配的参数都填充到该参数对象上。

模拟请求

模拟请求
① 省略 value 属性值自动匹配或创建
@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(@ModelAttribute Person person) {
    System.out.println(person);
    //Person{name="null", age=13}
    return "success";
}

注:在执行 testModelAttribute(..) 方法时,因为参数属性是一个 Person 类对象,那么他先从 model 里面找(没有指明 value 属性值,则以该类名首字母小写为 key),发现找不到便创建一个,把请求里面的参数赋值到该创建对象上,找到了则用请求里面的参数更新该对象。

② 指定 value 属性值匹配或创建
@ModelAttribute(value = "p")
public Person modelAttributeMethod3(@RequestParam Integer age) {
    return new Person("超哥哥 3 号", age);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(@ModelAttribute(value = "p") Person person) {
    System.out.println(person);
    //Person{name="超哥哥 3 号", age=13}
    return "success";
}

注:在执行 testModelAttribute(..) 方法时,因为参数属性是一个 Person 类对象,那么他先从 model 里面找(有指明 value 属性值,则以 value 属性值为 key),发现找不到便创建一个,把请求里面的参数赋值到该创建对象上,找到了则用请求里面的参数更新该对象。

③ 省略 @ModelAttribute 注解的 POJO 参数
@ModelAttribute
public Person modelAttributeMethod3(@RequestParam Integer age) {
    return new Person("超哥哥 4 号", age);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(Person person) {
    System.out.println(person);
    //Person{name="超哥哥 4 号", age=13}
    return "success";
}

注:@ModelAttribute 注解修饰的方法,没有指定 value 属性,则自动注入到 model 里面的 value 以该对象类名首字母小写为 key。在下面 @RequestMapping 修饰的方法 testModelAttribute(..) 参数时一个 POJO 对象,虽前面没有注解修饰,但默认也会去匹配 ModelAttributeMethodProcessor 参数解析器去解析该参数,说白了与上面的第一种情况 @ModelAttribute 注解修饰没有设置 value 属性值是一样的。

八、在Controller中使用redirect方式处理请求

forword:表示转发!
redirect:表示重定向!

@RequestMapping(value = "index")
public String index() {
    return "success";
}
@RequestMapping(value = "index")
public String index() {
    return "redirect:success";
}
九、RESTFul 风格的 SpringMVC 1.RESTFulController
@RequestMapping(value = "rest")
@Controller
public class RESTFulController {

    @RequestMapping(value = {"home", "/", ""}, method = RequestMethod.GET)
    public String goResetHome() {
        System.out.println("访问了 Rest 风格测试首页");
        return "8-rest";
    }

    @RequestMapping(value = "student/{id}", method = RequestMethod.GET)
    public String get(@PathVariable(value = "id") Integer id) {
        System.out.println("get " + id);
        return "success";
    }

    @RequestMapping(value = "student/{id}", method = RequestMethod.POST)
    public String post(@PathVariable(value = "id") Integer id) {
        System.out.println("post " + id);
        return "success";
    }

    @RequestMapping(value = "student/{id}", method = RequestMethod.PUT)
    public String put(@PathVariable(value = "id") Integer id) {
        System.out.println("put " + id);
        return "success";
    }

    @RequestMapping(value = "student/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable(value = "id") Integer id) {
        System.out.println("delete " + id);
        return "success";
    }
}
2.web.xml中配置

form表单发送put和delete请求,需要在web.xml中进行如下配置



  hiddenHttpMethodFilter
  org.springframework.web.filter.HiddenHttpMethodFilter


  hiddenHttpMethodFilter
  /*
3.模拟请求
十、@RequestBody 和 @ResponseBody

在SpringMVC的 Controller 中经常会用到 @RequestBody@ResponseBody 这两个注解,若想使用这两个注解,前提要写好 标签,他会帮我们注入接下里解析需要的转换器。

1.@RequestBody

简介:
@RequestBody 注解用于修饰 Controller 的方法参数,根据 HTTP Request Header 的 content-Type 的内容,通过适当的 HttpMessageConverter 转换为 Java 类。

使用时机:
当提交的数据不是普通表单的形式(application/x-www-form-urlcodedmultipart/form-data),而是 JSON 格式(application/json) 或 XML 格式(application/xml)。

使用示例:XML格式数据提交

POJO 模型类

@XmlRootElement(name = "person")
public class Person {
    private String name;
    private Integer age;

    public String getName() { return name; }
    @XmlElement
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    @XmlElement
    public void setAge(Integer age) { this.age = age; }
}

AJAX 请求

点击事件

Controller 里对应的方法

@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public String testRequestBody(@RequestBody Person person) {
    System.out.println(person);
    //Person{name="Tom", age=13}
    return "success";
}

注:@RequestBody 注解对于XML请求数据的解析,请求方要指定 Content-Type = application/xml;charset=utf-8,服务器如果要将接收数据封装成 POJO 类,需要在该 POJO 类里面用 @XmlRootElement@XmlElement 注解指明跟标签和子标签,SpringMVC 内部最终用到的是自带的 Jaxb2RootElementHttpMessageConverter 转换器(其实现了 HttpMessageConverter 接口)。

2.@ResponseBody

简介:
@ResponseBody 注解用于修饰 Controller 的方法,根据 HTTP Request Header 的 Accept 的内容,通过适当的 HttpMessageConverter 转换为客户端需要格式的数据并且写入到 Responsebody 数据区,从而不通过视图解析器直接将数据响应给客户端。

使用时机:
返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用。

使用示例:XML格式数据响应

POJO 模型类

@XmlRootElement(name = "person")
public class Person {
    private String name;
    private Integer age;

    public String getName() { return name; }
    @XmlElement
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    @XmlElement
    public void setAge(Integer age) { this.age = age; }
}

Controller 里对应的方法

@ResponseBody
@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public Person testRequestBody() {
    Person person = new Person("Tom",13);
    return person;
}

AJAX 请求

点击事件

最终浏览器控制台输出

注:@ResponseBody 注解对于响应XML格式数据的解析,请求方要指定 Accept = application/xml;charset=utf-8,服务器如果想将 POJO 类转换成XML格式数据,需要在该 POJO 类里面用 @XmlRootElement@XmlElement 注解指明跟标签和子标签,SpringMVC 内部最终用到的是自带的 Jaxb2RootElementHttpMessageConverter 转换器(其实现了 HttpMessageConverter 接口)。

3.原理简介

@RequestBody@ResponseBody 注解最终匹配到的参数解析器和返回值解析器都是 RequestResponseBodyMethodProcessor 对象,所以该对象分别实现了 HandlerMethodArgumentResolverHandlerMethodReturnValueHandler 接口。
在该解析器中有一个 messageConverters 属性,该属性是用来记录转换器的 List,这些转换器都是在该解析器初始化的时候 标签帮我们注入的。并且这些解析器都实现了 HttpMessageConverter 接口,在 HttpMessageConverter 接口中有四个最为主要的接口方法。

public interface HttpMessageConverter {
    boolean canRead(Class clazz, @Nullable MediaType mediaType);
    T read(Class clazz, HttpInputMessage inputMessage);

    boolean canWrite(Class clazz, @Nullable MediaType mediaType);
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
}

read 对应请求输入的转换解析,write 对应响应输出的转换解析。
canRead 根据 Request Header 的 content-Type 的内容查看该 HttpMessageConverter 换器是否支持转换,支持则转换为对应的 Java 类绑定到修饰的方法入参上。
canWrite 根据 Request Headers 里面的 Accept 的内容查看该 HttpMessageConverter 换器是否支持转换,支持则转换成指定格式后,写入到 Response 对象的 body 数据区。

对应流程图如下

十一、解析和返回 Json 数据 1.支持架包导入

首先需要导入 JSON 支持架包并且注入转换器


  com.fasterxml.jackson.core
  jackson-databind
  2.9.6

jackson-databind-2.9.6.jar 架包依赖于 jackson-annotations-2.9.0.jarjackson-core-2.9.6.jar,所以省略了依赖架包的手动导入。

同时要写好 标签,其会帮我们注入对应的JSON数据转换器。

2.代码示例

需要封装的 POJO

public class Person {
    private String name;
    private Integer age;
}

Controller中对应的请求方法

@ResponseBody
@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public Person testRequestBody(@RequestBody Person person) {
    System.out.println(person);
    return person;
}

注:参数用 @RequestBody 修饰意思是将请求的JSON数据用合适的转换器,转换成 Java 类。@ResponseBody 注解是将返回的数据通过合适的转换器转换成客户端想要的样子并返回,在这里是将请求解析的 Person 对象转换成JOSN格式数据并返回。

AJAX 请求

点击事件

注:① 发送的数据要是JSON格式(也就是 data 属性的数据是JSON格式);② 指明请求数据为JSON格式(contentType: "application/json;charset=utf-8");③ 指明接收数据为JSON格式(headers: { Accept: "application/json;charset=utf-8" })。

3. 原理简介

最终使用到的转换器是 jackson 提供的 MappingJackson2HttpMessageConverter,也是在解析器初始化的时候 标签帮我们注入的。

十二、文件上传 1.支持架包导入

为了实现文件上传,需要导入 commons-fileupload 架包,导入如下



    commons-fileupload
    commons-fileupload
    1.3.3
2.配置MultipartResolver

SpringMVC 上下文中默认没有装配 MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用SpringMVC的文件上传功能,则需要在上下文中配置 MultipartResolver。在SpringMVC配置文件中进行如下配置


    
    
    
    

注:这里一定要设置 id,并且值必须是 multipartResolver,下面的简单原理会解释。

3.代码示例

Controller 中对应的方法

@RequestMapping(value = "upload", method = RequestMethod.POST)
public String testUpload(@RequestParam(value = "file") MultipartFile multipartFile, HttpServletRequest request) throws Exception {
    if (multipartFile.isEmpty() == false) {
        //multipartFile.getName()   标签名字
        //multipartFile.getOriginalFilename()  上传文件名字
        //multipartFile.getSize()   上传文件大小
        //multipartFile.getContentType()    上传文件类型

        //在 webapp 目录下面(项目目录下面) 建立一个 resources 资源文件夹, 用来存储上传的资源文件
        String parent = request.getServletContext().getRealPath("/resources");
        String filename = UUID.randomUUID() + multipartFile.getOriginalFilename();

        File file = new File(parent, filename);
        multipartFile.transferTo(file);
    }
    return "success";
}

JSP页面的可变表单请求


4.原理简介

DispatcherServlet 初始化的时候,会从容器中加载 MultipartResolver 可变表单解析器,从下面源码中可以看出加载条件就是 idname 为 multipartResolver 的 bean

接着简单了解下解析,在 DispatcherServletdoDispatch(..) 方法中检查该请求是否是可变表单请求,如果是则用加载到缓存的 MultipartResolver 解析器 (这里用到的是注入容器中的 CommonsMultipartResolver 可变表单解析器,其实现了 MultipartResolver 接口) 将可变请求解析成 MultipartFile 对象 (这里是 CommonsMultipartFile,其实现了MultipartFile 接口),放在 HttpServletRequest 对象中,最终通过合适的参数解析器绑定到对应方法的参数上。

十三、文件下载

SpringMVC提供了一个 ResponseEntity 类型,使用它可以很方便地定义返回的 HttpHeadersHttpStatus
以下代码演示文件的下载功能

@RequestMapping(value = "download", method = RequestMethod.GET)
public ResponseEntity testDownload(HttpServletRequest request, @RequestParam String filename) throws Exception {

    String parent = request.getServletContext().getRealPath("/resources");
    File file = new File(parent, filename);

    byte[] body = FileUtils.readFileToByteArray(file);

    String downloadFilename = new String(file.getName().getBytes("utf-8"), "iso-8859-1");

    HttpHeaders headers = new HttpHeaders();
    //设置文件类型
    headers.add("Content-Disposition", "attchement;filename=" + downloadFilename);

    ResponseEntity responseEntity = new ResponseEntity(body, headers, HttpStatus.OK);
    return responseEntity;
}
十四、拦截器

SpringMVC的处理器拦截器,类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。

1.过滤器与拦截器区别

过滤器:依赖于servlet容器,在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。

拦截器:依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个 Controller 生命周期之内可以多次调用。

2.拦截器接口

拦截器一个有3个回调方法,而一般的过滤器Filter才两个:

preHandle预处理回调方法,实现处理器的预处理。返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断,不会继续调用其他的拦截器或处理器,此时我们需要通过 response 来产生响应;

postHandle后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过 modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理。

afterCompletion整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于 try-catch-finally 中的finally

3.代码编写

有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor 接口的话,三个方法必须实现,此时 SpringMVC 提供了一个 HandlerInterceptorAdapter 适配器(一种适配器设计模式的实现),允许我们只实现需要的回调方法,该适配器内部实现了 HandlerInterceptor 接口。

先写两个拦截器

public class HandlerInterceptor1 extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("HandlerInterceptor1 preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor1 postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("HandlerInterceptor1 afterCompletion");
    }
}
public class HandlerInterceptor2 extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("HandlerInterceptor2 preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor2 postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("HandlerInterceptor2 afterCompletion");
    }
}

拦截器的注入


    
    

Controller 方法编写

@RequestMapping(value = "/hello")
public String testHello() {
    System.out.println("HelloController.testHello");
    return "success";
}

最终输出看下执行顺序

HandlerInterceptor1 preHandle
HandlerInterceptor2 preHandle
HelloController.testHello
HandlerInterceptor2 postHandle
HandlerInterceptor1 postHandle
HandlerInterceptor2 afterCompletion
HandlerInterceptor1 afterCompletion
4.运行流程图

5.选择性拦截注入

有的时候我们需要拦截器拦截指定的请求,这样也是可以配置的


    
        
        
        
        
        
    

    
        
        
        
    
十五、异常处理

在SpringMVC中,所有用于处理在请求映射和请求处理过程中抛出的异常的类,都要实现 HandlerExceptionResolver 接口。
一个基于SpringMVC的Web应用程序中,可以存在多个实现了 HandlerExceptionResolver 的异常处理类,他们的执行顺序是由其 order 的值从小到大来先后执行,直到遇到返回的 ModelAndView 不为空则终断接下来的异常解析器的执行并返回异常的 ModelAndView 对象。

标签会帮我们注入常用的三个异常解析器:ExceptionHandlerExceptionResolverResponseStatusExceptionResolverDefaultHandlerExceptionResolver

但是我们接下来重点是了解下常用的两个异常解析器,分别是:ExceptionHandlerExceptionResolverSimpleMappingExceptionResolver

1.ExceptionHandlerExceptionResolver

注意 @ExceptionHandler 注解修饰的方法里面,只能自己 newModelAndView 对象然后装入需要的注入的值,对于传参里面带的 ModelModelMap 达不到传值要求。

① 异常处理方法写在对应的类里面

这样只能处理该 Controller 里面的异常

处理该 Controller 里面所有的异常,在没有找到指定的异常类对应的处理方法的前提下

@ExceptionHandler
public ModelAndView handlerAllException(Exception e) {
    ModelAndView mv = new ModelAndView();
    mv.addObject("exceptionMsg", e.getMessage());
    mv.setViewName("error");
    System.out.println("HelloController.handlerAllException");
    return mv;
}

处理该 Controller 里面指定类型的异常

@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView handlerArithmeticException(Exception e) {
    ModelAndView mv = new ModelAndView();
    mv.addObject("exceptionMsg", e.getMessage());
    mv.setViewName("error");
    System.out.println("HelloController.handlerArithmeticException");
    return mv;
}
② 异常处理方法写在多带带的异常处理类里面

这样可以处理所有 Controller 的异常,而不是针对单个的 Controller 类,类上需要用 @ControllerAdvice 注解修饰。

@ControllerAdvice
public class HandlerException {
    @ExceptionHandler
    public ModelAndView handlerAllException(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("exceptionMsg", e.getMessage());
        mv.setViewName("error");
        System.out.println("HelloController.handlerAllException");
        return mv;
    }
    @ExceptionHandler(value = {ArithmeticException.class})
    public ModelAndView handlerArithmeticException(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("exceptionMsg", e.getMessage());
        mv.setViewName("error");
        System.out.println("HelloController.handlerArithmeticException");
        return mv;
    }
}
2.SimpleMappingExceptionResolver

不用自己写java类处理异常,直接配置就可以了


    
    
    
        
            error
        
    
十六、整合SpringIOC和SpringMVC

在 web.xml 中配置 contextLoaderListener,并且加入spring的配置文件 applicationContext.xml

这样可以把 service、dao、事务、缓存、以及和其它框架的整合放到 spring 的配置文件里面
web.xml 文件配置如下





    
    
        contextConfigLocation
        classpath:applicationContext.xml
    
    
        org.springframework.web.context.ContextLoaderListener
    
    
    
    
        dispatcherServlet
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            classpath:spring-mvc.xml
        
        1
    

    
        dispatcherServlet
        /
    

在 web.xml 中配置 SpringMVC 的 Servlet 和加入 springmvc.xml,这时两个配置文件中扫描的包有重合的时候出现某些bean会被初始化2次的问题。

解决:在扫描包的子节点下配置 exclude-filterinclude-filter

SpringMVC 只扫描 @Controller@ControllerAdvice


    
    

Spring排除扫描 @Controller@ControllerAdvice


    
    

注意:Spring 和 SpringMVC 都有一个 IOC 容器,并且Controller 类的 bean 在 SpringMVC 的 IOC 容器中,但是它可以引用 Spring 的 IOC 容器中的 bean 如 service 和 dao 层的 bean,反之则不行,因为 Spring IOC 容器和 SpringMVC IOC 容器是父子关系,相当于全局变量和局部变量的关系!

十七、SpringMVC运行流程

其他相关文章

SpringMVC入门笔记
SpringMVC工作原理之处理映射HandlerMapping
SpringMVC工作原理之适配器HandlerAdapter
SpringMVC工作原理之参数解析
SpringMVC之自定义参数解析
SpringMVC工作原理之视图解析及自定义
SpingMVC之标签

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73032.html

相关文章

  • SpringMVC入门笔记

    摘要:入门笔记简介是一种基于的实现了设计模式的请求驱动类型的轻量级框架,是系开源项目中的一个,和配合使用。配置在中需要添加使用的和映射规则。入门较快,而掌握起来相对较难。 SpringMVC入门笔记 1. 简介 Spring MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架 ,是Spring系开源项目中的一个,和IoC配合使用。通过策略接口,Spring...

    zhaochunqi 评论0 收藏0
  • Java学习路线总结,搬砖工逆袭Java架构师(全网最强)

    摘要:哪吒社区技能树打卡打卡贴函数式接口简介领域优质创作者哪吒公众号作者架构师奋斗者扫描主页左侧二维码,加入群聊,一起学习一起进步欢迎点赞收藏留言前情提要无意间听到领导们的谈话,现在公司的现状是码农太多,但能独立带队的人太少,简而言之,不缺干 ? 哪吒社区Java技能树打卡 【打卡贴 day2...

    Scorpion 评论0 收藏0
  • SpringMVC入门就这么简单

    摘要:也就是说映射器就是用于处理什么样的请求提交给处理。这和是一样的提交参数的用户名编号提交配置处理请求注册映射器包框架接收参数设置无参构造器,里边调用方法,传入要封装的对象这里的对象就表示已经封装好的了对象了。 什么是SpringMVC? SpringMVC是Spring家族的一员,Spring是将现在开发中流行的组件进行组合而成的一个框架!它用在基于MVC的表现层开发,类似于struts...

    SKYZACK 评论0 收藏0
  • 慕课网_《SpringMVC数据绑定入门》学习总结

    摘要:数据绑定入门学习总结时间年月日星期日说明本文部分内容均来自慕课网。慕课网教学示例源码个人学习源码第一章课程介绍数据绑定入门概述数据绑定概念来自百度百科简单绑定是将一个用户界面元素控件的属性绑定到一个类型对象实例上的某个属性的方法。 《SpringMVC数据绑定入门》学习总结 时间:2017年2月19日星期日说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.co...

    Karrdy 评论0 收藏0
  • angularjs+springMvc学习笔记

    摘要:回调说白了,就是把函数当参数传给另一根函数,在另一个函数执行时调用此函数例如,在下面这段代码中,上面定义了两个函数和,下面的方法请求成功执行,失败执行异步异步的原理我看了网上的一些博客和例子,大都以定时任务为例子说明,但具体的原理我还是不太 回调 说白了,就是把函数当参数传给另一根函数,在另一个函数执行时调用此函数例如,在下面这段代码中,上面定义了两个函数success和error,下...

    dreamGong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<