Skip to content

SpringMVC中不常用注解使用

@InitBinder

在参数绑定时进行可以针对复杂对象自定义参数绑定逻辑,比如:

java
@GetMapping("/testInitBinder")
public String testInitBinder(Date date){
    return date.toString();
}

此时如果访问:http://localhost:8080/tuling-web/app/testInitBinder?date=1111-1-1,会报错:

java
Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; 
nested exception is org.springframework.core.convert.ConversionFailedException: 
Failed to convert from type [java.lang.String] to type [java.util.Date] for value '1111-1-1'; 
nested exception is java.lang.IllegalArgumentException]

此时可以在当前Controller中添加:

java
@InitBinder
public void initBinder(WebDataBinder binder) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
	dateFormat.setLenient(false);
	binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

有了这个就相当于添加了一个自定义的日期格式转换器,这样就能正常访问了。

注意,如果我们想把String类型转成我们自定义的User类,那么在User类中得提供一个String类型的构造方法

关键类为:ObjectToObjectConverter、TypeConverterDelegate:

  1. 在处理请求时,在调用方法之前,会把方法参数类型作为target类型,String类型作为source类型
  2. 会先匹配到ObjectToObjectConverter,除非你自定定义了一个String类型->target类型的Converter
  3. 在ObjectToObjectConverter中会需要target类型中有一个String参数的构造方法才算匹配到,不然ObjectToObjectConverter都不给你匹配,就不能完成转换了
  4. 匹配到ObjectToObjectConverter后,会先调用TypeConverterDelegate的convertIfNecessary()方法
  5. 在这个方法中就会找到我们注册的CustomEditor,并且利用它把String类型转成target类型

在Controller中定义的InitBinder只会在本Controller中生效,我们可以通过ControllerAdvice来定义一个全局的:

java
@ControllerAdvice
public class ZhouyuControllerAdvice {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }
}

@SessionAttributes

只能写在类上,通过@SessionAttributes注解指定model中哪些key的value存到session中。

java
@RestController
@SessionAttributes(names = {"user"})
public class ZhouyuController {

	@GetMapping("/test")
	public String test(Model model) {
		model.addAttribute("user", "xxxx");
		return null;
	}

	@GetMapping("/testSession")
	public String testSession(HttpServletRequest httpServletRequest){
		Object user = httpServletRequest.getSession().getAttribute("user");
		return (String) user;
	}

}

@RequestAttribute 与 @SessionAttribute

都只能写在方法参数前面,表示从相对应request.getAttribute()、session.getAttribute()中获取值传递给方法参数

@ModelAttribute

可以写在某个方法上,@ModelAttribute可以定义在一个Controller中,当请求这个Controller中的方法时,会先调用@ModelAttribute所修饰的方法,方法返回的值会添加到model中,比如以下代码就会向model中添加一个key为user,values为请求中user参数所传递的值。

在访问以下两个GetMapping时,都会先执行addString方法,向model中添加值,有点类似切面的作用了。

java
@ModelAttribute("user")
public String addString(@RequestParam("user") String value){
    return value;
}

@GetMapping("/testModelAttribute")
public String testModelAttribute(Model model){
    return (String) model.getAttribute("user");
}

@GetMapping("/testModelAttributev2")
public String testModelAttributeV2(Model model){
    return (String) model.getAttribute("user");
}

我们同样可以定义在ControllerAdvice中,从而变成全局的:

java
@ControllerAdvice
public class ZhouyuControllerAdvice {

	@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(User.class, new UserEditor());
	}

	@ModelAttribute("user")
	public String addString(@RequestParam("user") String value){
		return value;
	}
    
}

也可以写在方法参数前,会将model中的参数值传递给方法参数,或者model中如果没有则会从request.getParameter()中获取对应的值设置给model

更新: 2022-12-11 16:30:14
原文: https://www.yuque.com/renyong-jmovm/spring/lezs4zrkv4sadn15