flyEn'blog

ControllerAdvice

@ControllerAdvice

从名字上可以看出大体意思是控制器增强。

Spring@ControllerAdvice原始码中有关的注解如下:

对于声明{@link ExceptionHandler @ExceptionHandler},{@link InitBinder @InitBinder}或{@link ModelAttribute @ModelAttribute}方法以在多个{@code @Controller}类之间共享的类,专门使用{@link Component @Component} 。

@ControllerAdvice是一个特殊的@Component,用于标识一个类,用于定义@ExceptionHandler@InitBinder@ModelAttribute方法。适用于所有使用@RequestMapping方法。

@InitBinder

注释,用于标识初始化{@link org.springframework.web.bind.WebDataBinder}的方法,这些方法将用于填充带注释的处理程序方法的命令和表单对象参数。此类init-binder方法支持{@link RequestMapping}支持的所有参数,但命令/表单对象和相应的验证结果对象除外。初始化绑定器方法不能具有返回值;它们通常被声明为{@code void}。

作用:注册属性编辑器,对HTTP请求参数进行处理,再绑定到对应的接口,某些格式化的时间转换等。替换@Controller类的方法上时,仅该类里的接口有效。与@ControllerAdvice组合使用可合并的能力。

1
2
3
4
5
6
7
8
@ControllerAdvice
public class ActionAdvice {

@InitBinder
public void handleException(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
}
}

@ExceptionHandler(🌟)

作用:统一异常处理,也可以指定要处理的异常类型

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
@ControllerAdvice
public class ActionAdvice {

@ExceptionHandler(Exception.class)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public Map handleException(Exception ex) {
Map<String, Object> map = new HashMap<>();
map.put("code", 400);
map.put("msg", ex.toString());
return map;
}
}

@ModelAttribute

作用:绑定数据

示例:

1
2
3
4
5
6
7
8
@ControllerAdvice
public class ActionAdvice {

@ModelAttribute
public void handleException(Model model) {
model.addAttribute("user", "zfh");
}
}

在接口中获取前面绑定的参数:

1
2
3
4
5
6
7
8
@RestController
public class BasicController {

@GetMapping(value = "index")
public Map index(@ModelAttribute("user") String user) {
//...
}
}

完整示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
* 统一异常处理
* @author zfh
* @version 1.0
* @since 2019/1/4 15:23
*/
@ControllerAdvice
public class ControllerExceptionHandler {

private Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);

@InitBinder
public void initMyBinder(WebDataBinder binder) {
// 添加对日期的统一处理
//binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));

// 添加表单验证
//binder.addValidators();
}

@ModelAttribute
public void addMyAttribute(Model model) {
model.addAttribute("user", "zfh"); // 在@RequestMapping的接口中使用@ModelAttribute("name") Object name获取
}

@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody // 如果使用了@RestControllerAdvice,这里就不需要@ResponseBody了
public Map handler(Exception ex) {
logger.error("统一异常处理", ex);
Map<String, Object> map = new HashMap<>();
map.put("code", 400);
map.put("msg", ex);
return map;
}
}

测试接口:

1
2
3
4
5
6
7
8
9
10
@RestController
public class TestAction {

@GetMapping(value = "testAdvice")
public JsonResult testAdvice(@ModelAttribute("user") String user, Date date) throws Exception {
System.out.println("user: " + user);
System.out.println("date: " + date);
throw new Exception("直接抛出异常");
}
}

高阶应用

格式化时间转日期

使用@ControllerAdvice+ @InitBinder,可将http请求参数中的时间自动转换成日期类型。

1
2
3
4
5
6
7
@InitBinder
public void initBinder(WebDataBinder binder) {
GenericConversionService genericConversionService = (GenericConversionService) binder.getConversionService();
if (genericConversionService != null) {
genericConversionService.addConverter(new DateConverter());
}
}

自定义的时间类型转换器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* 日期转换类
* 将标准日期、标准日期时间、时间戳转换成Date类型
*/
public class DateConverter implements Converter<String, Date> {
private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
private static final String shortDateFormat = "yyyy-MM-dd";
private static final String timeStampFormat = "^\\d+$";

@Override
public Date convert(String value) {

if(StringUtils.isEmpty(value)) {
return null;
}

value = value.trim();

try {
if (value.contains("-")) {
SimpleDateFormat formatter;
if (value.contains(":")) {
formatter = new SimpleDateFormat(dateFormat);
} else {
formatter = new SimpleDateFormat(shortDateFormat);
}
return formatter.parse(value);
} else if (value.matches(timeStampFormat)) {
Long lDate = new Long(value);
return new Date(lDate);
}
} catch (Exception e) {
throw new RuntimeException(String.format("parser %s to Date fail", value));
}
throw new RuntimeException(String.format("parser %s to Date fail", value));
}
}

扩展:

@RestControllerAdvice = @ControllerAdvice+ @ResponseBody

原文:https://juejin.cn/post/6844903826412011533

Fork me on GitHub