@ControllerAdvice 使用场景
@ControllerAdvice
是Spring 框架中的注解,多用在Spring MVC应用程序中。
使用场景1:处理异常
# 示例1
import org.apache.ibatis.javassist.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)@ResponseBodypublic ResponseEntity<String> handleException(Exception ex) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred");}@ExceptionHandler(NotFoundException.class)public ResponseEntity<String> handleNotFoundException(NotFoundException ex) {return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Resource not found");}
}
使用场景2:修改返回值
通过实现接口ResponseBodyAdvice
来修改返回值,并直接作为 ResponseBody 类型处理器 的返回值。
# 示例2
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.util.Map;@ControllerAdvice
public class HelloResponseBodyAdvice implements ResponseBodyAdvice<Map<String, Object>> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return true;}@Overridepublic Map<String, Object> beforeBodyWrite(Map<String, Object> body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {// 修改返回值System.out.println("origin map: " + body);body.put("msg", "hello");return body;}
}
# 示例3
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import static java.util.Objects.nonNull;@ControllerAdvice
public class HttpResponseBodyAdvice implements ResponseBodyAdvice {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class clazz,ServerHttpRequest request, ServerHttpResponse response) {HttpHeaders headers = response.getHeaders();// 分页信息添加到ServerHttpResponseHttpHeaders headersContext = ResponseUtils.getHeaders();if (nonNull(headersContext) && !headersContext.isEmpty()) {headers.addAll(headersContext);}// 状态码添加到ServerHttpResponseif (nonNull(ResponseUtils.getResponseCode())) {response.setStatusCode(ResponseUtils.getResponseCode());}return body;}
}
验证
CASE1:只有1个 HelloResponseBodyAdvice
时
@RestController
@RequestMapping("/test")
public class ResponseBodyAdviceController {@RequestMapping(value = "/hello", method = RequestMethod.GET)public Map<String, Object> hello() {Map<String, Object> map = new HashMap<>();map.put("a", 1);map.put("b", 2);return map;}
}
结论: 返回值被 成功修改
CASE2:有2个 ResponseBodyAdvice 时
结论:则均执行,且按照类名字母降序的顺序执行。比如
HelloResponseBodyAdvice
比 PracResponseBodyAdvice
先执行,
APracResponseBodyAdvice
比 HelloResponseBodyAdvice
先执行。
CASE3:发生异常时
@RestController
@RequestMapping("/test")
public class ResponseBodyAdviceController {@RequestMapping(value = "/hello", method = RequestMethod.GET)public Map<String, Object> hello() {int a = 0;int b = 5;int i = b / a;return null;}
}
结论:执行顺序为:GlobalExceptionHandler
→ HelloResponseBodyAdvice
→ HttpResponseBodyAdvice
结论:异常也可被修改值,所以这个修改值的操作,慎用!!! 一般微服务只有 2 个 @ControllerAdvice
,一个用作修改 HttpHeader 和 HttpStatus (示例3
),一个用作处理异常(示例1
)。
CASE4:@RestController
VS @Controller
@Controller
@Slf4j
public class SearchController {@AutowiredUserService userService;@RequestMapping(value = "/getAllStudents", method = RequestMethod.GET)public List<Student> login() {List<Student> students = userService.listStudents(1, 10);students.forEach(System.out::println);return students;}
}
结论:@RestController
会成功返回结果, @Controller
不会返回结果