SpringBoot之@ControllerAdvice,@ModelAttribute,@InitBinder详解

@ControllerAdvice

在 SpringBoot 中,@ControllerAdvice 通常结合 @ExceptionHandler 注解处理全局异常,全局异常处理详见第 13 章;通过@ControllerAdvice,我们可以将控制器的全局配置放置在同一个位置,注解了@Controller 的类的方法可使用 @ExceptionHandler、@InitBinder、@ModelAttribute 注解到方法上,这对所有注解了@RequestMapping 的控制器内的方法有效

@ModelAttribute

该注解应用位置包括下面几种:

(1) 应用在方法上

(2) 应用在方法的参数上

(3) 应用在方法上,并且方法也使用了@RequestMapping

2.1 应用在方法上
被 @ModelAttribute 注解的方法会在 Controller 每个方法执行之前都执行,因此对于一个 Controller中包含多个URL的时候,要谨慎使用

@Controller
@RequestMapping("/modelattributeTest")
public class ModelAttributeTestController1 {
    // 下面的代码也可以放在添加了 @ControllerAdvice 的类中, 那样就会作用于所有的 Controller
    @ModelAttribute
    public void myModel(@RequestParam(required = false) String abc, Model model) {
        model.addAttribute("attributeName", abc);
    }

    @RequestMapping(value = "/test1")
    public String test1() {
        return "modelattributetest/test1";
    }
}

在请求 /modelattributeTest/test1?abc=aaa 后,会先执行 myModel 方法,然后接着执行 test1 方法,参数 abc 的值被放入 Model 中后,接着被带到 test1 方法中;当返回视图 modelattributetest/test1 时,Model 会被带到页面上,当然在使用 @RequestParam 的时候可以使用 required 来指定参数是否是必须的;如果把 myModel 和 test1 合二为一后的方法也可以,这是我们最常用的方法

@RequestMapping(value = "/test2")
public String test1(@RequestParam(required = false) String abc, Model model) {
    model.addAttribute("attributeName", abc);
    return "modelattributetest/test1";
}

此外,使用 @ModelAttribute 注解带有返回值的方法如下

// 方法一
@ModelAttribute
public String myModel(@RequestParam(required = false) String abc) {
    return abc;
}
// 方法二
@ModelAttribute
public Student myModel(@RequestParam(required = false) String abc) {
    Student stu = new Student(abc);
    return stu;
}
// 方法三
@ModelAttribute
public int myModel(@RequestParam(required = false) int number) {
    return number;
}

这种情况,返回值对象会被默认放到隐含的 Model 中,在 Model 中的 key 为返回值类型(首字母小写),value 为返回的值;那么对应上面三个方法和下面这三个写法等价

model.addAttribute("string", abc);
model.addAttribute("student", stu);
model.addAttribute("int", number);

自定义 key,只需要给 @ModelAttribute 添加 value 属性即可,如下所示;相当于 model.addAttribute(“num”, number);

@ModelAttribute(value = "num") 
public int myModel(@RequestParam(required = false) int number) {    
    return number;
}

2.2 使用 @ModelAttribute 注解方法的参数

@ModelAttribute(value = "num")
public int myModel(@RequestParam(required = false) int number) {
    return number;
}

相当于:model.addAttribute(“attributeName”, abc);

补充,顺便提及一下 @SessionAttributes 的使用:

(1) 如果在类上面使用了 @SessionAttributes(“attributeName”) 注解,而本类中恰巧存在attributeName,则会将 attributeName 放入到 session 作用域

(2) 如果在类上面使用了 @SessionAttributes(“attributeName”) 注解,SpringMVC 会在执行方法之前,自动从 session 中读取 key 为 attributeName 的值,并注入到 Model 中

所以我们在方法的参数中使用 @ModelAttribute(“attributeName”) 就会正常的从 Model 读取这个值,也就相当于获取了 session中的值

(3) 使用了 @SessionAttributes 之后,Spring 无法知道什么时候要清掉 @SessionAttributes 存进去的数据,如果要明确告知,也就是在方法中传入 SessionStatus 对象参数,并调 status.setComplete() 就可以了

注意:@SessionAttributes 需要清除时,使用 SessionStatus.setComplete(); 来清除;注意,它只清除 @SessionAttributes 的 session,不会清除 HttpSession 的数据;可参考:https://blog.csdn.net/hayre/article/details/54666275

2.3 应用在方法上,并且方法也使用了 @RequestMapping 注解

@Controller
@RequestMapping("/modelattributeTest4")
public class ModelAttributeTestController4 {
    @RequestMapping(value = "/test1")
    @ModelAttribute("name")
    public String test1(@RequestParam(required = false) String name) {
        return name;
    }
}

这种情况下,返回值 String(或者其他对象),就不再是视图了;将是我们上面将到的放入 Model 中的值,此时对应的页面就是 @RequestMapping 的 value 值 test1;等价于

model.addAttribute("name", name);
return "test1";

2.4 全局设置 @ModelAttribute,需要结合 @ControllerAdvice 使用,新建一个类,添加如下代码测试

@ControllerAdvice
public class GlobalExceptionHandler {
    // 全局添加属性值
    @ModelAttribute
    public void newUser(Model model) {
        System.out.println("... @ModelAttribute 应用到所有@RequestMapping注解方法, 
            在其执行之前把返回值放入Model...");    
        model.addAttribute("temp", new User("dufu", "666", new Date()));
    }

    // 或者如下这样,这样就相当于 mode.addAttribue("temp", new User("dufu", "666", new Date()))
    @ModelAttribute("temp")
    public User newUser() {
        System.out.println("... @ModelAttribute 应用到所有@RequestMapping注解方法,
                在其执行之前把返回值放入Model...");    
        return new User("dufu", "666", new Date()));
    }
}

所有使用了 @RequestMapping 或者类似的注解(如:@GetMapping)的方法都会被作用到;我们就可以在页面上获取到设置的值

注意:如果要在 Controller 里获取 @ModelAttribute 设置的值,可参考如下代码

@GetMapping("/user")
public User getUser(@ModelAttribue("temp")User user) {
    return user;
}

@InitBinder

多用作转换器,主要用于对添加了@Controller 的类,@RequestMapping 注解的方法,在方法执行之前执行;主要作用于 GET 请求,常用来校验接收的值,设置不返回的值,或者初始化传递来的值(常用来对前端传递来的日期的转化处理)

示例,结合 @ControllerAdvice 注解将前端传递的 http://xxx?user=ramos:123 这样的字符串转化为 user 对象

@ControllerAdvice
public class GlobalExceptionHandler {
    // 全局初始化值
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(User.class, new UserEditor());
        System.out.println("... @InitBinder 应用到所有@RequestMapping注解方法,
            在其执行之前初始化数据绑定器...");    
    }
}

上面的代码,我们添加了一个转化器,用于把前端传递过来的字符串转化成对应的 User 对象;那么需要添加一个转化器:UserEditor,注意自定义的转化器需要继承 PropertyEditorSupport

public class UserEditor extends PropertyEditorSupport {
    @Override
    public String getAsText() {
        return super.getAsText();
    }
   
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if (!StringUtils.isEmpty(text)) {
            User user = new User();
            String[] items = text.split(":");
            user.setUsername(items[0]);
            user.setPassword(items[1]);
            user.setCreateDate(new Date());
            setValue(user);
        } else {
            throw new CommonException("传递的字符串为空");
        }
    }
}

Contorller 层调用,注意这里是 Get 请求,接收的值却是 User 对象;@RequestParam 注解不可少

@GetMapping(value="/name}")
@ResponseBody
public User getUserByName(@RequestParam("user") User user) {
    System.out.println(user);
    return user;
}

前端传递格式:http://localhost:8080/user/name/andy?user=ramos:123,这样就能把字符串转化成 user 对象;相当于一个转换器

除了使用 @ControllerAdvice 来配置全局的 WebDataBinder,还可以使用 RequestMappingHandlerAdapter;如下是一个格式化时间的 dataBinder 的实现

@Bean
public RequestMappingHandlerAdapter webBindingInitializer() {
    RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
    adapter.setWebBindingInitializer(new WebBindingInitializer() {@
        Override
        public void initBinder(WebDataBinder binder, WebRequest request) {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            binder.registerCustomEditor(Date.class, new CustomDateEditor(format, false));
        }
    });
    return adapter;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值