@ControllerAdvice
Spring源码中有关@ControllerAdvice
的注解如下:
Specialization of {@link Component @Component}
for classes that declare {@link ExceptionHandler @ExceptionHandler},
{@link InitBinder @InitBinder},
or {@link ModelAttribute @ModelAttribute} methods
to be shared across multiple {@code @Controller} classes.
理解:
@ControllerAdvice
是一个特殊的@Component
,用于标识一个类,这个类中被以下三种注解标识的方法:@ExceptionHandler,@InitBinder,@ModelAttribute
,将作用于所有的@Controller
类的接口上。
so,这三个注解有啥用呢?
@InitBinder
Annotation that identifies methods which initialize the {
@link org.springframework.web.bind.WebDataBinder} which will be used for
populating command and form object arguments of annotated handler methods.
Such init-binder methods support all arguments that {@link RequestMapping} supports,
except for command/form objects and
corresponding validation result objects.
Init-binder methods must not have a return value;
they are usually declared as {@code void}.
作用:注册属性编辑器,对HTTP请求参数进行处理,再绑定到对应的接口,比如格式化的时间转换等。应用于单个@Controller类的方法上时,仅对该类里的接口有效。与@ControllerAdvice组合使用可全局生效。
e.g.
/**
* 全局日期绑定,只能应对普通表单提交,对@RequestBody无效
*/
@ControllerAdvice
public class DateBindAdvice {
private static final String[] FORMATS =
{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM"};
@InitBinder
public void dateBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new DatePropertyEditor());
}
/**
* 日期转换器
*/
private class DatePropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (StringUtils.isBlank(text)) {
return;
}
Date date;
try {
date = DateUtils.parseDate(text, FORMATS);//这个代码就不贴了,网上一大把
} catch (Exception e) {
throw new IllegalArgumentException("日期格式不正确 '" + text + "'", e);
}
setValue(date);
}
}
}
PropertyEditorSupport
类自行百度
@ExceptionHandler
作用:统一异常处理,也可以指定要处理的异常类型
e.g.
/**
* 全局异常处理
*/
@RestControllerAdvice
@Slf4j
public class ExceptionAdvice {
private static final Logger logEmail = LoggerFactory.getLogger("email");
/**
* 405 not support异常
*/
@ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class})
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public LiveResp onNotSupportedException(Exception e,
HttpServletRequest request) {
String uri = request.getRequestURI();
log.error("uri:{},code:{},message:{}", uri, NOT_SUPPORTED.getCode(), e.getMessage());
return createErrorResp(NOT_SUPPORTED, null);
}
/**
* 404 not support异常
*/
@ExceptionHandler(value = NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public LiveResp onException(NoHandlerFoundException e, HttpServletRequest request) {
String uri = request.getRequestURI();
log.error("uri:{},code:{},message:{}", uri, NOT_FOUND.getCode(), e.getMessage());
return createErrorResp(NOT_FOUND, null);
}
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public LiveResp onException(Exception e, HttpServletRequest request) {
String uri = request.getRequestURI();
String params = JSONObject.toJSONString(request.getParameterMap());
if (e instanceof SQLException || e instanceof DataAccessException) {
createLog(e, uri, params);
return createErrorResp(DB_ERROR, null);
} else if (e instanceof FatalException) {//异常这块回头再讲
FatalException ex = (FatalException) e;
// 异步发送邮件
logEmail.error(ex.getMessage(), ex);
createLog(e, uri, params);
return createErrorResp(ex.getMsgCode(), ex.getMessage());
} else if (e instanceof BaseException) {
BaseException ex = (BaseException) e;
log.error("uri:{},params:{},code:{},message:{}", uri, params, ex.getMsgCode().getCode(),
ex.getMessage());
return createErrorResp(ex.getMsgCode(), ex.getMessage());
} else if (e instanceof MissingServletRequestParameterException
|| e instanceof BindException
|| e instanceof ConstraintViolationException
|| e instanceof TypeMismatchException
|| e instanceof ServletRequestBindingException) {
createLog(e, uri, params);
return createErrorResp(PARAM_ERROR, null);
} else {
createLog(e, uri, params);
return createErrorResp(SERVER_ERROR, null);
}
}
private LiveResp createErrorResp(MsgCode msgCode, String message) {
return new LiveResp(msgCode.getCode(), isNotBlank(message) ? message : msgCode.getMessage());
}
private void createLog(Exception e, String uri, String params) {
log.error("uri:" + uri + ",params:" + params, e);
}
}
@ResponseStatus
是个标识,标识拦截哪个异常 不懂的自己百度
其中LiveResp
是个统一包装返回类
/**
* 统一返回包装
*/
@Data
@Accessors(chain = true)//这是lombok的东西 有兴趣自行百度
public class LiveResp<T> {
private int code = 200;
private String msg = "";
private T data;
private String traceId = TraceUtil.getTraceId();//回头搞
/**
* 响应时间戳
*/
private Long timestamp = System.currentTimeMillis();//回头加上
public LiveResp() {
}
public LiveResp(T data) {
this.data = data;
}
public LiveResp(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
/**
* 系统异常
*/
public class FatalException extends BaseException {
public FatalException(String message) {
this(CommMsgCode.SYSTEM_ERROR, message);
}
public FatalException(String message, Throwable cause) {
this(CommMsgCode.SYSTEM_ERROR, message, cause);
}
public FatalException(MsgCode code) {
super(code);
}
public FatalException(MsgCode code, String message) {
super(code, message);
}
public FatalException(MsgCode code, String message, Throwable cause) {
super(code, message, cause);
}
public FatalException(MsgCode code, Throwable cause) {
super(code, cause);
}
public FatalException(MsgCode code, String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(code, message, cause, enableSuppression, writableStackTrace);
}
}
public class BaseException extends RuntimeException {
private final MsgCode msgCode;
public BaseException(MsgCode msgCode) {
this.msgCode = msgCode;
}
public BaseException(String message) {
super(message);
this.msgCode = CommMsgCode.SERVICE_ERROR;
}
public BaseException(MsgCode msgCode, String message) {
super(message);
this.msgCode = msgCode;
}
public BaseException(MsgCode msgCode, String message, Throwable cause) {
super(message, cause);
this.msgCode = msgCode;
}
public BaseException(MsgCode msgCode, Throwable cause) {
super(cause);
this.msgCode = msgCode;
}
public BaseException(MsgCode msgCode, String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
this.msgCode = msgCode;
}
public MsgCode getMsgCode() {
return msgCode;
}
@Override
public String getMessage() {
String msg = super.getMessage();
if (msg == null || msg.trim().length() == 0) {
msg = msgCode.getMessage();
}
return msg;
}
}
/**
* Service 异常
*/
public class ServiceException extends BaseException {
public ServiceException(String message) {
this(CommMsgCode.SERVER_ERROR, message);
}
public ServiceException(String message, Throwable cause) {
this(CommMsgCode.SERVER_ERROR, message, cause);
}
public ServiceException(MsgCode code) {
super(code);
}
public ServiceException(MsgCode code, String message) {
super(code, message);
}
public ServiceException(MsgCode code, String message, Throwable cause) {
super(code, message, cause);
}
public ServiceException(MsgCode code, Throwable cause) {
super(code, cause);
}
public ServiceException(MsgCode code, String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(code, message, cause, enableSuppression, writableStackTrace);
}
}
/**
* 通用错误码定义
*
*/
public enum CommMsgCode implements MsgCode {
/**
* 协议级返回码
*/
UPGRADE(100, "您当前版本需要升级"),
UPGRADE_DATA(101, "需更新配置或数据"),
SUCCESS(200, "操作成功"),
REDIRECT(302, "重定向"),
PARAM_ERROR(400, "请求参数错误"),
UNAUTHORIZED(401, "没有权限"),
FORBIDDEN(403, "禁止访问"),
NOT_FOUND(404, "资源不存在"),
NOT_SUPPORTED(405, "Method Not SUPPORTED"),
SERVER_ERROR(500, "内部服务器错误"),
ROUTER(700, "路由"),
/**
* 系统级错误码
*/
SYSTEM_ERROR(1001, "系统错误"),
SERVICE_PAUSE(1002, "服务暂停"),
SERVICE_BUSY(1003, "服务器忙"),
/**
* 业务级错误码
*/
WEB_ERROR(110000, "服务端web异常"),
SERVICE_ERROR(120000, "服务端service异常"),
DAO_ERROR(130000, "服务端dao异常"),
DB_ERROR(191000, "数据库访问异常"),
IO_ERROR(192000, "IO操作异常"),
CACHE_ERROR(193000, "cache操作异常"),
LOCAL_INVOKE_ERROR(194000, "Local api调用错误"),
RPC_INVOKE_ERROR(195000, "RPC调用错误"),
SECURITY_ERROR(196000, "安全错误"),
NO_DATA(197000, "没有数据"),
INVALID_SIGN(198000, "签名错误"),
INVALID_TOKEN(198401, "invalid token"),
/**
* 其它错误码
*/
OTHER_ERROR(900000, "其它错误");
private int code;
private String message;
CommMsgCode(int code, String message) {
this.code = code;
this.message = message;
}
@Override
public int getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
@ModelAttribute
先mark一下,没怎么看懂
@RequestBody
请求怎么搞
上面讲了非@RequestBody
请求的处理,那么@RequestBody
请求怎么搞,往下看
/**
* 针对@RequestBody请求的处理 代码很简单 自己看
*/
@ControllerAdvice
@Slf4j
public class LiveRequestBodyAdvice implements RequestBodyAdvice {
private static final String DEFAULT_CHARSET = "UTF-8";
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
// 可以在些写过滤条件
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
String body = IOUtils.toString(inputMessage.getBody(), DEFAULT_CHARSET);
log.info("request body:{}", body);
return new InputMessage(inputMessage.getHeaders(),
new ByteArrayInputStream(body.getBytes(DEFAULT_CHARSET)));
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage,
MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
private static class InputMessage implements HttpInputMessage {
private HttpHeaders headers;
private InputStream body;
public InputMessage(HttpHeaders headers, InputStream body) {
this.headers = headers;
this.body = body;
}
@Override
public InputStream getBody() throws IOException {
return body;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}
}
@ControllerAdvice
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE) //标识bean加载的顺序
public class LiveRespBodyAdvice implements ResponseBodyAdvice<Object> {
public static final String LIFE_PACKAGE = "org.bjf.modules";
@Override
public boolean supports(MethodParameter methodParameter,
Class<? extends HttpMessageConverter<?>> converterType) {
String className = methodParameter.getContainingClass().getName();
return className.startsWith(LIFE_PACKAGE) &&
!LiveResp.class.isAssignableFrom(methodParameter.getParameterType()) && !String.class
.isAssignableFrom(methodParameter.getParameterType());
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
String path = request.getURI().getPath();
log.debug("uri:{},response data:{}", path, JSONObject.toJSONString(body));
return new LiveResp(body != null ? body : "");
}
}
以上