Spring MVC 进一步学习

1.SpringMvc是什么

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框 架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。

Spring Web MVC也是服务到工作者模式的实现,但进行可优化。前端控制器是DispatcherServlet;应用控制器其实拆为处理器映射器(Handler Mapping)进行处理器管理和视图解析器(View Resolver)进行视图管理;页面控制器/动作/处理器为Controller接口(仅包含ModelAndView handleRequest(request, response) 方法)的实现(也可以是任何的POJO类);支持本地化(Locale)解析、主题(Theme)解析及文件上传等;提供了非常灵活的数据验证、格式化和数据绑定机制;提供了强大的约定大于配置(惯例优先原则)的契约式编程支持。

2.Spring Web MVC 能干什么

√让我们能非常简单的设计出干净的Web层和薄薄的Web层;

√进行更简洁的Web层的开发;

√天生与Spring框架集成(如IoC容器、AOP等);

√提供强大的约定大于配置的契约式编程支持;

√能简单的进行Web层的单元测试;

√支持灵活的URL到页面控制器的映射;

√非常容易与其他视图技术集成,如Velocity、Thymeleaf、FreeMarker等等,因为模型数据不放在特定的API里,而是放在一个Model里(Map 数据结构实现,因此很容易被其他框架使用);

√非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API;

√提供一套强大的JSP标签库,简化JSP开发;

√支持灵活的本地化、主题等解析;

√更加简单的异常处理;

√对静态资源的支持;

√支持Restful风格。

3.Spring Web MVC 核心架构

核心架构的具体流程步骤如下:

1、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

2、 DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个 HandlerInterceptor拦截器)对象,通过这种策略模式,很容 易添加新的映射策略;

3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);

5、 ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;

6、 View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;

7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

@PathVariable

取得 URL 路径中匹配的内容,适合 RESTful 的风格。

package com.toltech.controller;


/**
 * @author Wgs
 * @version 1.0
 * @create:2018/05/09
 * @PathVariable 取得 URL 路径中匹配的内容,适合 RESTful 的风格。
 */
@Controller
public class ParameterController {
    // {userId} 是 placeholder,里面的内容可以用 @PathVariable 取到
    @RequestMapping("/users/{userId}")
    @ResponseBody
    public String one(@PathVariable Integer userId) {
        return userId + "";
    }
    // 如果变量名和 {} 中的名字不一样,
    // 需要用 @PathVariable("nameInPath") 来指定变量要使用路径中的哪一个变量
    @RequestMapping("/categories/{categoryName}/products/{productId}")
    @ResponseBody
    public String two(@PathVariable String categoryName,
                      @PathVariable("productId") Integer pId) {
        return "categoryName: " + categoryName + ", productId: " + pId;
    }

    // 还支持正则表达式的方式
    @RequestMapping("/regex/{text:[a-z]+}-{number:\\d+}")
    @ResponseBody
    public String three(@PathVariable String text, @PathVariable Integer number) {
        return "Text: " + text + ", Number: " + number;
    }

    // 路径中有 . 时需要用正则,如http://localhost:8080//ion-i/2.jpg
    
    @GetMapping("/question-img/{subjectCode}/{imageName:.+}")
    public void readQuestionImage(@PathVariable String subjectCode, @PathVariable String imageName) {
        System.out.println(subjectCode+"----"+imageName);
    }


}
测试:
  • http://localhost:8080/users/1234
  • http://localhost:8080/categories/xbox/products/1234
  • http://localhost:8080/regex/part-1234

@RequestParam

取得 HTTP 请求 中的参数(GET 和 POST 都可以)。

package com.toltech.controller;



/**
 * @author Wgs
 * @version 1.0
 * @create:2018/05/09
 * @PathVariable 取得 URL 路径中匹配的内容,适合 RESTful 的风格。
 */
@Controller
public class ParameterController {
    // 取得名字为 id 的参数的值
    @RequestMapping("/user1")
    @ResponseBody
    public String findUser(@RequestParam Integer id) {
        return "ID: " + id;
    }
    // required 默认是 true,参数是必要的,如果没有提供需要的参数,则报错
    // required 为 false 表示参数是可选的
    @RequestMapping("/product1")
    @ResponseBody
    public String findProduct(@RequestParam(value="productId", required=true) Integer id,
                              @RequestParam(value="productName", required=false) String name) {
        return "ID: " + id + ", Name: " + name;
    }
}
测试:
  • http://localhost:8080/user?id=1234
  • http://localhost:8080/product?productId=1234&productName=PS4


@Controller
@RequestMapping("/customer")
public class CustomerController {

    @GetMapping
    public String list(Model model,
                       @RequestParam(name = "p",defaultValue = "1") Integer pageNo) {
        System.out.println("PageNO: " + pageNo);
        model.addAttribute("pageNo",pageNo);
        return "customer/list";
    }

SpringMVC 数据绑定

SpringMVC 中提供了多种数据绑定,可以把请求中的数据绑定为简单类型,简单数组,对象,对象的数组等。

简单数组
  • http://localhost:8080/array?name=Tom&name=Lucy
  • URL 中多个同名的参数映射为同一个数组的元素,例如 name
  • 数组是 primitive, String 等简单类型的数
@RequestMapping("/array")
@ResponseBody
public String[] array(@RequestParam("name") String[] names) {
    return names;
}

@RequestMapping("/list")
@ResponseBody
public List list(@RequestParam("name") List names) {
    return names;
}
显示
[“Tom”,”Lucy”]

简单对象

  • http://localhost:8080/object?username=Tom&age=10
  • URL 中参数名和对象中的属性名一样的会自动映射
  • 对 User 进行映射
@RequestMapping("/object")
@ResponseBody
public User object(User user) {
    return user;
}

显示
{
    "username": "Tom",
    "age": 10,
    "address": null
}

复杂对象

  • http://localhost:8080/object?username=Tom&password=Passw0rd&address.city=Beijing&address.street=SCI
  • 可以使用级连映射,URL 的参数名为属性的级连,例如 address.city,映射的是 user 的 address 的 city 属性
@RequestMapping("/nested-object")
@ResponseBody
public User object(User user) {
    return user;
}

显示
{
    "username": "Tom",
    "age": 10,
    "address": {
        "city": "Beijing",
        "street": "SCI"
    }
}

同属性多对象

  • http://localhost:8080/intersect-object?user.username=Tom&admin.username=Jim&age=10
  • User 和 Admin 都有同名的属性 username, age,可以使用 WebDataBinder 对其区分
  • 映射的参数名 user 和 @InitBinder 中的 value user 要一样

如果 URL 都使用 username 和 age 的话,会同时作用于 user 和 admin,使用 WebDataBinder 可对其加前缀,就能在映射的时候区分开是给谁使用的。
user.username and admin.username 分别映射到对象 user 和 admin,age 同时映射到他们的 age 属性上。

@RequestMapping("/intersect-object")
@ResponseBody
public Map intersectObject(User user, Admin admin) {
    Map map = new HashMap();
    map.put("user", user);
    map.put("admin", admin);
    return map;
}
@InitBinder("user")
public void initUser(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("user.");
}
@InitBinder("admin")
public void initAdmin(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("admin.");
}
显示:


{
    "admin": {
        "username": "Jim",
        "age": 10
    },
    "user": {
        "username": "Tom",
        "age": 10,
        "address": null
    }
}

对象数组

  • http://localhost:8080/object-list?users[0].username=Tom&users[1].username=Lucy
  • 不能直接使用 List users 来映射
  • 需要创建一个类 UserList,其属性为 List users,然后使用级连映射相似的方式来映射,但是要有数组的下标
@RequestMapping("/object-list")
@ResponseBody
public UserList objectList(UserList userList) {
    return userList;
}
显示:

{
    "users": [{
        "username": "Tom",
        "age": 0,
        "address": null
    }, {
        "username": "Lucy",
        "age": 0,
        "address": null
    }]
}

对象的 Map

  • http://localhost:8080/object-map?users['x'].username=Tom&users['y'].username=Lucy

  • http://localhost:8080/object-map?users["x"].username=Tom&users["y"].username=Lucy

  • 不能直接使用 Map users 来映射

  • 需要创建一个类 UserMap,其属性为 Map users,然后使用级连映射相似的方式来映射,但是要有 Map 的 key

@RequestMapping("/object-map")
@ResponseBody
public UserMap objectMap(UserMap users) {
    return users;
}
显示:

{
    "users": {
        "x": {
            "username": "Tom",
            "age": 0,
            "address": null
        },
        "y": {
            "username": "Lucy",
            "age": 0,
            "address": null
        }
    }
}

AJAX 数据绑定

如果是 AJAX 传递数据给服务器端,然后要绑定为对象,那么 AJAX 不能是 GET 操作,如果只是使用 AJAX 从服务器端获取数据,则可以是 GET,POST 等。




    
    
    




接受 AJAX 传递过来的数据,映射为对象时必须要有 @RequestBody 修饰方法的参数

@RequestMapping("/json-bind")
@ResponseBody
public User json(@RequestBody User user) {
System.out.println(user.getUsername());
return user;
}

浏览器控制台输出

{
    username: "Biao",
    age: 111,
    address: null
}

需要的类

User:

public class User {
    private String username;
    private int age;
    private Address address;
    // Setters and getters
}
Address:

public class Address {
    private String city;
    private String street;
    // Setters and getters
}
Admin: 

public class Admin {
    private String username;
    private int age;
    // Setters and getters
}

UserList:

public class UserList {
    private List users;
    // Setters and getters
}
UserMap:

public class UserMap {
    private Map users;
    // Setters and getters
}


@ModelAttribute

把 HTTP 请求的参数映射到对象,参数名和对象中的属性名匹配的就做映射,不匹配的就不管,此注解可以省略。

package com.xtuer.controller;

@Controller
public class ParameterController {
    // 表单中的参数被映射到对象 user
    @RequestMapping("/user")
    @ResponseBody
    public String findUser(@ModelAttribute User user,
                           @RequestParam(required=false) Integer age) {
        System.out.println("Age: " + age);
        return user.toString();
    }
}
package com.xtuer.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class User {
    private int id;
    private String username;
    private String password;
}
测试:
  • http://localhost:8080/user?id=1234&username=biao
  • http://localhost:8080/user?id=1234&username=biao&password=Secret
  • http://localhost:8080/user?id=1234&username=biao&password=Secret&age=12

Forward and Redirect

Forward 在 url 前面加上字符串 forward: 即可
Redirect 在 url 前面加上字符串 redirect: 即可

package com.xtuer.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ParameterController {
    @RequestMapping("/forward-test")
    public String forward() {
        return "forward:/helloworld-springmvc";
    }
    @RequestMapping("/redirect-test")
    public String redirect() {
        return "redirect:/helloworld-springmvc";
    }
}


测试:
  • http://localhost:8080/forward-test
  • http://localhost:8080/redirect-test

RedirectAttributes

表单提交后一般都会 redirect 到另一个页面,防止表单重复提交。RedirectAttributes 的作用就是把处理 PageA 的结果存储起来,当 redirect 到 PageB 的时候显示 PageA 的结果。
Request 中的参数不能被传递给 redirect 的页面,因为 redirect 是从浏览器端发起一个新的请求。

防止表单重复提交的流程:

Spring MVC 进一步学习_第1张图片
image




    Update User


    
Username:
Password:



Result: ${result}

package com.xtuer.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
public class ParameterController {
    // 显示表单
    @RequestMapping("/user-form")
    public String showUserForm() {
        return "user-form.html";
    }
    // 更新 User,把结果保存到 RedirectAttributes
    @RequestMapping("/update-user")
    public String updateUser(@RequestParam String username,
                             @RequestParam String password,
                             final RedirectAttributes redirectAttributes) {
        // Update user in database...
        System.out.println("Username: " + username + ", Password: " + password);
        // 操作结果显示给用户
        redirectAttributes.addFlashAttribute("result", "The user is already successfully updated");
        return "redirect:/result";
    }
    // 显示表单处理结果
    @RequestMapping("/result")
    public String result() {
        return "result.html";
    }
}
测试:

访问 http://localhost:8080/user-form,填写信息,提交表单,处理好后页面被 redirect 到 http://localhost:8080/result 显示操作结果。

Spring MVC 进一步学习_第2张图片
image

Redirect 到另一个 Controller 时获取 RedirectAttributes 里的属性使用 @ModelAttribute


@RequestMapping("/flash")
public String flash(RedirectAttributes redirectAttributes) {
    redirectAttributes.addFlashAttribute("username", "Biao");
    return "redirect:flash2";
}
@RequestMapping("/flash2")
@ResponseBody
public String flash2(@ModelAttribute("username") String username) {
    return "username: " + username;
}

获取 Request and Response

想取得 HttpServletRequest 和 HttpServletResponse 很容易,只要在方法的参数里定义后,Spring MVC 会自动的注入它们到参数里。

package com.xtuer.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class ParameterController {
    @RequestMapping("/request-response")
    @ResponseBody
    public String foo(HttpServletRequest request, HttpServletResponse response) {
        System.out.println(request);
        return "WoW";
    }
}
测试:
  • http://localhost:8080/request-response

Header

读取 header: The @RequestHeader annotation allows a method parameter to be bound to a request header
写入 header: Header 直接写入到 HttpServletResponse,没有注解用来写 header

下面是浏览器发送请求后的 header 信息:
Header  Value
Host    localhost:8080
Accept  text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset  ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive  300


package com.xtuer.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
@Controller
public class ParameterController {
    @RequestMapping("/read-header")
    @ResponseBody
    public String readHeader(@RequestHeader("Host") String host,
                             @RequestHeader(value="Accept-Encoding", required=false) String encoding,
                             @RequestHeader(value="Accept-Charset", required=false) String charset) {
        return "Host: " + host + ", Encoding: " + encoding + ", Charset: " + charset;
    }
    @RequestMapping("/read-header-error")
    @ResponseBody
    public String readHeaderError(@RequestHeader("Host") String host,
                                  @RequestHeader("Accept-Charset") String charset) {
        return "Host: " + host + ", Charset: " + charset;
    }
    @RequestMapping("/write-header")
    @ResponseBody
    public String writeHeader(HttpServletResponse response) {
        response.setHeader("token", "D4BFCEC2-89E6-40CB-AF9A-B5513CB30FED");
        return "Header is wrote.";
    }
}
测试:
  • http://localhost:8080/read-header
  • http://localhost:8080/read-header-error
  • http://localhost:8080/write-header

访问 http://localhost:8080/read-header 成功访问

Spring MVC 进一步学习_第3张图片
image

访问 http://localhost:8080/read-header-error 提示错误,因为 charset 是 required=true 的,在 header 信息里没有 charset,所以报错。但是这个错误如果不了解的话会一头雾水,因为在控制台中没有输出错误的原因

Spring MVC 进一步学习_第4张图片
image
参数错误,类型转换错误等默认在控制台中看不到,也不会返回给浏览器端,要看到错误原因,需要把 Spring MVC 的日志调到 Debug 级别。

Cookie

  • 读取 cookie: The @CookieValue annotation allows a method parameter to be bound to the value of an HTTP cookie
  • 写入 cookie: Cookie 直接写到 HttpServletResponse,没有注解用来写 cookie
package com.xtuer.controller;

import javax.servlet.http.HttpServletResponse

@Controller
public class ParameterController {
    @RequestMapping("/read-cookie")
    @ResponseBody
    public String readCookie(@CookieValue("username") String cookie) {
        return "Cookie for username: " + cookie;
    }
    @RequestMapping("/write-cookie")
    @ResponseBody
    public String writeCookie(HttpServletResponse response) {
        Cookie cookie = new Cookie("username", "Don't tell you");
        cookie.setMaxAge(1000);
        response.addCookie(cookie); // Put cookie in response.
        return "Cookie is wrote.";
    }
}
测试:
  • 访问 http://localhost:8080/read-cookie 报错,因为还没有 cookie,
    可以给 cookie 一个默认值,这样即使没有 cookie 也不会报错了:
@CookieValue(value="username", defaultValue="") String cookie
  • 访问 http://localhost:8080/write-cookie 写入 cookie

  • 访问 http://localhost:8080/read-cookie 输出 cookie


package com.kaishengit.controller;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

@Controller
public class FileUploadController {

    @GetMapping("/upload")
    public String uploadFile(@RequestHeader(name = "User-Agent") String userAgent,
                             HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        System.out.println("userAgent:" + userAgent);

        Cookie cookie = new Cookie("userLevel","SVIP");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(60 * 60 * 24);
        cookie.setDomain("localhost");

        response.addCookie(cookie);
        return "upload";
    }

    @PostMapping("/upload")
    public String uploadFile(@CookieValue(name = "userLevel") String userLevel, String name, MultipartFile photo, RedirectAttributes redirectAttributes) throws IOException {
        System.out.println("cookieValue: " + userLevel);
        if(!photo.isEmpty()) {
            System.out.println("表单中的元素名字:" + photo.getName());
            System.out.println("原始文件名称:" + photo.getOriginalFilename());
            System.out.println("ContentType:" + photo.getContentType());
            System.out.println("文件大小:" + photo.getSize() + " -> " + FileUtils.byteCountToDisplaySize(photo.getSize()));

            //byte[] bytes = photo.getBytes();

            /*InputStream inputStream = photo.getInputStream();
            OutputStream outputStream = new FileOutputStream("D:/temp/upload/"+photo.getOriginalFilename());
            IOUtils.copy(inputStream,outputStream);

            outputStream.flush();
            outputStream.close();
            inputStream.close();*/
            photo.transferTo(new File("X:/temp/upload/"+photo.getOriginalFilename()));


        } else {
            System.out.println("请选择文件");

            redirectAttributes.addFlashAttribute("message","请选择文件");
        }
        return "redirect:/upload";
    }

    @ExceptionHandler(IOException.class)
    public String ioExceptionHandler() {
        return "error/500";
    }

}


Session

  • 可以使用 HttpSession 来直接操作 session
  • 同时也提供了操作 session 的注解 @SessionAttributes

HttpSession 的方式什么时候都生效,但是 @SessionAttributes 有时候就不行,如返回 AJAX 请求时设置的 session 无效。所以推荐使用注入 HttpSession 来读写 session。

package com.xtuer.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
@Controller
@SessionAttributes({"result"})
public class SessionController {
    @RequestMapping("/write-session")
    @ResponseBody
    public String writeSession(HttpSession session) {
        session.setAttribute("username", "座山雕");
        session.setAttribute("password", "天王盖地虎");
        return "Session wrote...";
    }
    @RequestMapping("/read-session")
    public String readSession() {
        return "helloworld-freemarker.htm";
    }
    @RequestMapping("/write-session2")
    public ModelAndView writeSession2() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("result", "Save session");
        mav.setViewName("user-form.htm");
        return mav;
    }
    @RequestMapping("/read-session2")
    public String readSession2() {
        return "result.htm";
    }
}
测试:
  • 访问 http://localhost:8080/write-session
  • 访问 http://localhost:8080/read-session

Spring MVC 的处理流程

  • 用户向服务器发送请求,请求被 Spring 前端控制 Servelt DispatcherServlet 捕获

  • DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回

  • DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。(附注:如果成功获得HandlerAdapter 后,此时将开始执行拦截器的 preHandler(…)方法)

  • 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)。 在填充Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:
    HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息
    数据转换:对请求消息进行数据转换,如 String 转换成 Integer、Double 等
    格式化:对请求消息进行数据格式化,如将字符串转换成格式化数字或格式化日期等
    数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中

  • Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象
    根据返回的 ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到 Spring 容器中的ViewResolver)返回给 DispatcherServlet

  • ViewResolver 结合 Model 和 View,来渲染视图

  • 将渲染结果返回给客户端

Spring MVC 进一步学习_第5张图片
image
Spring MVC 进一步学习_第6张图片
image
Spring MVC 进一步学习_第7张图片
image
Spring MVC 进一步学习_第8张图片
image

Spring MVC 教程, 快速入门, 深入分析

SpringMVC 获取 Request 和 Response

SpringMVC 中在任意地方取得 HttpServletRequest 和 HttpServletResponse

  1. 在 web.xml 中注册 RequestContextListener (SpringMVC 4 不需要这一步)
  
      
        org.springframework.web.context.request.RequestContextListener  
      

  1. 获取 HttpServletRequest 和 HttpServletResponse

public static String testRequestAndResponse() {
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    
    return request.getParameter("name");
}

SpringMVC 接收日期参数

SpringMvc 的请求中的参数(字符串)默认是不能自动地转换为日期的,需要使用 Converter, InitBinder 或者 Formatter 来把请求中的参数转换为日期。

使用 Converter

  • 定义字符串转换为日期的类 DateConverter
package com.xtuer.converter;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateConverter implements Converter {
    @Override
    public Date convert(String source) {
        String pattern = source.length()==10 ? "yyyy-MM-dd" : "yyyy-MM-dd HH:mm:ss";
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        try {
            return format.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}

  • SpringMvc 的配置文件中注册 Converter




    
        
            
        
    




  • 处理请求


@GetMapping("/to-date")
@ResponseBody
public Date toDate(Date date) {
    System.out.println("=========>" + date);
    return date;
}

  • 访问 http://localhost:8080/to-date?date=2016-04-12 或者 http://localhost:8080/to-date?date=2016-04-12%2012:12:12 能得到日期2016-04-12 和 2016-04-12 12:12:12

Converter 是全局的,可以在所有 Controller 中使用。

使用 InitBinder

  • Controller 中定义 InitBinder

@InitBinder("date")
public void initDate(WebDataBinder binder) {
    binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}

  • 处理请求


@GetMapping("/to-date")
@ResponseBody
public Date toDate(Date date) {
    System.out.println("=========>" + date);
    return date;
}

注意: @InitBinder(“date”) 中 date 必须和 toDate(Date date) 中的 date 名字一样,当然,请求的参数中也必须有名为 date 的参数。

@InitBinder 只能在当前 Controller 中使用,当有多个地方都需要把参数转换为日期对象,则使用 Converter 更适合。

你可能感兴趣的:(Spring MVC 进一步学习)