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);
    }


}
测试:

@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;
    }
}
测试:


@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 中提供了多种数据绑定,可以把请求中的数据绑定为简单类型,简单数组,对象,对象的数组等。

简单数组
@RequestMapping("/array")
@ResponseBody
public String[] array(@RequestParam("name") String[] names) {
    return names;
}

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

简单对象

@RequestMapping("/object")
@ResponseBody
public User object(User user) {
    return user;
}

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

复杂对象

@RequestMapping("/nested-object")
@ResponseBody
public User object(User user) {
    return user;
}

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

同属性多对象

如果 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<String, Object> map = new HashMap<String, Object>();
    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
    }
}

对象数组

@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

@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 等。

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
    <script>
    $(document).ready(function() {
        var data = {username: 'Biao', age: 111};
        $.ajax({
            url: '/json-bind',
            type: 'POST', // 1. 不能是 GET
            dataType: 'json',
            contentType: 'application/json', // 2. 少了就会报错
            data: JSON.stringify(data) // 3. data 需要序列化一下
        })
        .done(function(result) {
            console.log(result);
        })
        .fail(function() {
            console.log("error");
        })
        .always(function() {
            console.log("complete");
        });
    });
    </script>
</head>
<body>
</body>
</html>
接受 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<User> users;
    // Setters and getters
}
UserMap:

public class UserMap {
    private Map<String, User> 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;
}
测试:

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";
    }
}


测试:

RedirectAttributes

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

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

image
<!-- 文件名: view/user-form.htm -->
<!DOCTYPE html>
<html>
<head>
    <title>Update User</title>
</head>
<body>
    <form action="/update-user" method="post">
        Username: <input type="text" name="username"><br>
        Password: <input type="text" name="password"><br>
        <button type="submit">Update User</button>
    </form>
</body>
</html>


<!-- 文件名: view/result.htm -->
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 显示操作结果。

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";
    }
}
测试:

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 成功访问

image

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

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.";
    }
}
测试:
@CookieValue(value="username", defaultValue="") String 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";
    }
}
测试:

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,来渲染视图

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

image
image
image
image

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

SpringMVC 获取 Request 和 Response

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

  1. 在 web.xml 中注册 RequestContextListener (SpringMVC 4 不需要这一步)
<listener>  
    <listener-class>  
        org.springframework.web.context.request.RequestContextListener  
    </listener-class>  
</listener>
  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<String, Date> {
    @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

<mvc:annotation-driven conversion-service="customConversionService">
</mvc:annotation-driven>
<bean id="customConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.xtuer.converter.DateConverter"/>
        </set>
    </property>
</bean>



  • 处理请求


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

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 更适合。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容