一、@RestControllerAdvice是什么
@RestControllerAdvice
是一個(gè)組合注解,由@ControllerAdvice
、@ResponseBody
組成,而@ControllerAdvice
繼承了@Component,因此@RestControllerAdvice
本質(zhì)上是個(gè)Component
,用于定義@ExceptionHandler
,@InitBinder
和@ModelAttribute
方法,適用于所有使用@RequestMapping
方法。
二、@RestControllerAdvice的特點(diǎn)
- 通過(guò)
@ControllerAdvice
注解可以將對(duì)于控制器的全局配置放在同一個(gè)位置。 - 注解了
@RestControllerAdvice
的類的方法可以使用@ExceptionHandler
、@InitBinder
、@ModelAttribute
注解到方法上。 -
@RestControllerAdvice
注解將作用在所有注解了@RequestMapping
的控制器的方法上。 -
@ExceptionHandler
:用于指定異常處理方法。當(dāng)與@RestControllerAdvice
配合使用時(shí),用于全局處理控制器里的異常。 -
@InitBinder
:用來(lái)設(shè)置WebDataBinder,用于自動(dòng)綁定前臺(tái)請(qǐng)求參數(shù)到Model中。 -
@ModelAttribute
:本來(lái)作用是綁定鍵值對(duì)到Model中,當(dāng)與@ControllerAdvice
配合使用時(shí),可以讓全局的@RequestMapping
都能獲得在此處設(shè)置的鍵值對(duì)
@ControllerAdvice
public class GlobalController{
//(1)全局?jǐn)?shù)據(jù)綁定
//應(yīng)用到所有@RequestMapping注解方法
//此處將鍵值對(duì)添加到全局,注解了@RequestMapping的方法都可以獲得此鍵值對(duì)
@ModelAttribute
public void addUser(Model model) {
model.addAttribute("msg", "此處將鍵值對(duì)添加到全局,注解了@RequestMapping的方法都可以獲得此鍵值對(duì)");
}
//(2)全局?jǐn)?shù)據(jù)預(yù)處理
//應(yīng)用到所有@RequestMapping注解方法,在其執(zhí)行之前初始化數(shù)據(jù)綁定器
//用來(lái)設(shè)置WebDataBinder
@InitBinder("user")
public void initBinder(WebDataBinder binder) {
}
// (3)全局異常處理
//應(yīng)用到所有@RequestMapping注解的方法,在其拋出Exception異常時(shí)執(zhí)行
//定義全局異常處理,value屬性可以過(guò)濾攔截指定異常,此處攔截所有的Exception
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
@ControllerAdvice可以指定 Controller 范圍
- basePackages: 指定一個(gè)或多個(gè)包,這些包及其子包下的所有 Controller 都被該 @ControllerAdvice 管理
@RestControllerAdvice(basePackages={"top.onething"})
@Slf4j
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
- basePackageClasses: 是 basePackages 的一種變形,指定一個(gè)或多個(gè) Controller 類,這些類所屬的包及其子包下的所有 Controller 都被該 @ControllerAdvice 管理
@RestControllerAdvice(basePackageClasses={TestController.class})
@Slf4j
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
- assignableTypes: 指定一個(gè)或多個(gè) Controller 類,這些類被該 @ControllerAdvice 管理
@RestControllerAdvice(assignableTypes={TestController.class})
@Slf4j
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
- annotations: 指定一個(gè)或多個(gè)注解,被這些注解所標(biāo)記的 Controller 會(huì)被該 @ControllerAdvice 管理
@ControllerAdvice(annotations = {TestAnnotation.class})
@Slf4j
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
三、@ExceptionHandler
我們可以搭配@ResponseStatus
:可以將某種異常映射為HTTP狀態(tài)碼
首先需要為自己的系統(tǒng)設(shè)計(jì)一個(gè)自定義的異常類,通過(guò)它來(lái)傳遞狀態(tài)碼。
/**
* 自定義異常
*/
public class SystemException extends RuntimeException{
private String code;//狀態(tài)碼
public SystemException(String message, String code) {
super(message);
this.code = code;
}
public String getCode() {
return code;
}
}
第一種思路,設(shè)計(jì)一個(gè)基類
/**
* 處理異常的類,需要處理異常的Controller直接繼承這個(gè)類
*/
public class BaseController {
/**
* 處理Controller拋出的異常
* @param e 異常實(shí)例
* @return Controller層的返回值
*/
@ExceptionHandler
@ResponseBody
public Object expHandler(Exception e){
if(e instanceof SystemException){
SystemException ex= (SystemException) e;
return WebResult.buildResult().status(ex.getCode())
.msg(ex.getMessage());
}else{
e.printStackTrace();
return WebResult.buildResult().status(Config.FAIL)
.msg("系統(tǒng)錯(cuò)誤");
}
}
}
之后所有需要異常處理的Controller都繼承這個(gè)類,從而獲取到異常處理的方法。
雖然這種方式可以解決問(wèn)題,但是極其不靈活,因?yàn)閯?dòng)用了繼承機(jī)制就只為獲取一個(gè)默認(rèn)的方法,這顯然是不好的。
第二種思路,將這個(gè)基類變?yōu)榻涌?,提供此方法的默認(rèn)實(shí)現(xiàn)(也就是接口中的default方法,java8開(kāi)始支持接口方法的默認(rèn)實(shí)現(xiàn))
/**
* 接口形式的異常處理
*/
public interface DataExceptionSolver {
@ExceptionHandler
@ResponseBody
default Object exceptionHandler(Exception e){
try {
throw e;
} catch (SystemException systemException) {
systemException.printStackTrace();
return WebResult.buildResult().status(systemException.getCode())
.msg(systemException.getMessage());
} catch (Exception e1){
e1.printStackTrace();
return WebResult.buildResult().status(Config.FAIL)
.msg("系統(tǒng)錯(cuò)誤");
}
}
}
這種方式雖然沒(méi)有占用繼承,但是也不是很優(yōu)雅,因?yàn)閹缀跛械腃ontroller都需要進(jìn)行異常處理,于是我每個(gè)Controller都需要去寫implement DataExceptionSolver,這顯然不是我真正想要的。況且這種方式依賴java8才有的語(yǔ)法,這是一個(gè)很大的局限。
第三種思路,使用加強(qiáng)Controller做全局異常處理。
來(lái)個(gè)案例
1、定義一個(gè)異常信息描述基礎(chǔ)信息接口類
public interface BaseErrorInfoInterface {
/** 錯(cuò)誤碼*/
String getResultCode();
/** 錯(cuò)誤描述*/
String getResultMsg();
}
2、定義一個(gè)枚舉類實(shí)現(xiàn)上面的異常信息描述接口
public enum CommonEnum implements BaseErrorInfoInterface {
// 數(shù)據(jù)操作錯(cuò)誤定義
SUCCESS("200", "成功!"),
BODY_NOT_MATCH("400","請(qǐng)求的數(shù)據(jù)格式不符!"),
SIGNATURE_NOT_MATCH("401","請(qǐng)求的數(shù)字簽名不匹配!"),
NOT_FOUND("404", "未找到該資源!"),
INTERNAL_SERVER_ERROR("500", "服務(wù)器內(nèi)部錯(cuò)誤!"),
SERVER_BUSY("503","服務(wù)器正忙,請(qǐng)稍后再試!");
/** 錯(cuò)誤碼 */
private String resultCode;
/** 錯(cuò)誤描述 */
private String resultMsg;
CommonEnum(String resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
@Override
public String getResultCode() {
return resultCode;
}
@Override
public String getResultMsg() {
return resultMsg;
}
}
3、定義一個(gè)自定義異常類,標(biāo)識(shí)業(yè)務(wù)系統(tǒng)出現(xiàn)的異常信息
public class BizException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 錯(cuò)誤碼
*/
protected String errorCode;
/**
* 錯(cuò)誤信息
*/
protected String errorMsg;
public BizException() {
super();
}
public BizException(BaseErrorInfoInterface errorInfoInterface) {
super(errorInfoInterface.getResultCode());
this.errorCode = errorInfoInterface.getResultCode();
this.errorMsg = errorInfoInterface.getResultMsg();
}
public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {
super(errorInfoInterface.getResultCode(), cause);
this.errorCode = errorInfoInterface.getResultCode();
this.errorMsg = errorInfoInterface.getResultMsg();
}
public BizException(String errorMsg) {
super(errorMsg);
this.errorMsg = errorMsg;
}
public BizException(String errorCode, String errorMsg) {
super(errorCode);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public BizException(String errorCode, String errorMsg, Throwable cause) {
super(errorCode, cause);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getMessage() {
return errorMsg;
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
4、定義一個(gè)統(tǒng)一結(jié)果返回?cái)?shù)據(jù)封裝類
public class ResultBody {
/**
* 響應(yīng)代碼
*/
private String code;
/**
* 響應(yīng)消息
*/
private String message;
/**
* 響應(yīng)結(jié)果
*/
private Object result;
public ResultBody() {
}
public ResultBody(BaseErrorInfoInterface errorInfo) {
this.code = errorInfo.getResultCode();
this.message = errorInfo.getResultMsg();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
/**
* 成功
*
* @return
*/
public static ResultBody success() {
return success(null);
}
/**
* 成功
* @param data
* @return
*/
public static ResultBody success(Object data) {
ResultBody rb = new ResultBody();
rb.setCode(CommonEnum.SUCCESS.getResultCode());
rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
rb.setResult(data);
return rb;
}
/**
* 失敗
*/
public static ResultBody error(BaseErrorInfoInterface errorInfo) {
ResultBody rb = new ResultBody();
rb.setCode(errorInfo.getResultCode());
rb.setMessage(errorInfo.getResultMsg());
rb.setResult(null);
return rb;
}
/**
* 失敗
*/
public static ResultBody error(String code, String message) {
ResultBody rb = new ResultBody();
rb.setCode(code);
rb.setMessage(message);
rb.setResult(null);
return rb;
}
/**
* 失敗
*/
public static ResultBody error( String message) {
ResultBody rb = new ResultBody();
rb.setCode("-1");
rb.setMessage(message);
rb.setResult(null);
return rb;
}
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
}
5、定義一個(gè)全局異常處理類
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 處理自定義的業(yè)務(wù)異常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = BizException.class)
@ResponseBody
public ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){
logger.error("發(fā)生業(yè)務(wù)異常!原因是:{}",e.getErrorMsg());
return ResultBody.error(e.getErrorCode(),e.getErrorMsg());
}
/**
* 處理空指針的異常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =NullPointerException.class)
@ResponseBody
public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){
logger.error("發(fā)生空指針異常!原因是:",e);
return ResultBody.error(CommonEnum.BODY_NOT_MATCH);
}
/**
* 處理其他異常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =Exception.class)
@ResponseBody
public ResultBody exceptionHandler(HttpServletRequest req, Exception e){
logger.error("未知異常!原因是:",e);
return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);
}
}
說(shuō)明:上面的代碼,使用了
@ControllerAdvice
和@ExceptionHandler
注解。其中@ControllerAdvice
的作用是開(kāi)啟對(duì)全局異常的捕獲,這個(gè)注解還可以通過(guò)assignableTypes
參數(shù)指定特定的Controller
類,讓異常處理類只處理特定類拋出的異常。@ExceptionHandler
注解,標(biāo)明了該處理方法體處理的異常類型。
四、@InitBinder
SpringMVC并不是能對(duì)所有類型的參數(shù)進(jìn)行綁定的,如果對(duì)日期Date類型參數(shù)進(jìn)行綁定,就會(huì)報(bào)錯(cuò)IllegalStateException
錯(cuò)誤。所以需要注冊(cè)一些類型綁定器用于對(duì)參數(shù)進(jìn)行綁定。InitBinder注解就有這個(gè)作用。
@InitBinder
public void dateTypeBinder(WebDataBinder webDataBinder){
//往數(shù)據(jù)綁定器中添加一個(gè)DateFormatter日期轉(zhuǎn)化器。
webDataBinder.addCustomFormatter(new DateFormatter("yyyy-mm-dd"));
}
使用@InitBinder 注冊(cè)的綁定器只有在當(dāng)前Controller中才有效,不會(huì)作用于其他Controller。
如果覺(jué)得在每個(gè)Controller里面寫太復(fù)雜,你可以寫個(gè)BaseController,讓其他Controller繼承該類
我們可以自定義格式轉(zhuǎn)化器,實(shí)現(xiàn)Formatter接口就可。還可以添加驗(yàn)證器等等。
public class StringFormatter implements Formatter<String> {
private static final String PREFIX = "prefix- ";
@Override
public String parse(String text, Locale locale) throws ParseException {
//所以String類型參數(shù)都加上一個(gè)前綴。
String result = PREFIX + text;
return result;
}
@Override
public String print(String object, Locale locale) {
return object;
}
}
然后添加到數(shù)據(jù)綁定器中
@InitBinder
public void dateTypeBinder(WebDataBinder webDataBinder){
//往數(shù)據(jù)綁定器中添加一個(gè)DateFormatter日期轉(zhuǎn)化器。
webDataBinder.addCustomFormatter(new DateFormatter("yyyy-mm-dd"));
// 添加一個(gè)String類型數(shù)據(jù)綁定器,作用是添加一個(gè)前綴
webDataBinder.addCustomFormatter(new StringFormatter());
}
當(dāng)然,你也可以自己注冊(cè)自定義的編輯器
自定義的編輯器類需要繼承org.springframework.beans.propertyeditors.PropertiesEditor;
并重寫其setAsText和getAsText兩個(gè)方法就行了
然后在InitBinder方法中注冊(cè)就行。
這里提供給大家一個(gè)Demo
public class BaseController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new MyDateEditor());
binder.registerCustomEditor(Double.class, new DoubleEditor());
binder.registerCustomEditor(Integer.class, new IntegerEditor());
}
private class MyDateEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = format.parse(text);
} catch (ParseException e) {
format = new SimpleDateFormat("yyyy-MM-dd");
try {
date = format.parse(text);
} catch (ParseException e1) {
}
}
setValue(date);
}
}
public class DoubleEditor extends PropertiesEditor {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (text == null || text.equals("")) {
text = "0";
}
setValue(Double.parseDouble(text));
}
@Override
public String getAsText() {
return getValue().toString();
}
}
public class IntegerEditor extends PropertiesEditor {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (text == null || text.equals("")) {
text = "0";
}
setValue(Integer.parseInt(text));
}
@Override
public String getAsText() {
return getValue().toString();
}
}
}
利用@InitBinder實(shí)現(xiàn)表單多對(duì)象傳遞小技巧
Student對(duì)象和Course對(duì)象:
public class Student implements Serializable{
String id;
String note;
//get..set....
}
public class Course implements Serializable{
String id;
String note;
//set..get...
}
HTML頁(yè)面:
<form action="/test/test" method="get">
<input type="text" name="student.id" value="student_id">
<input type="text" name="student.name" value="student_name">
<input type="text" name="course.id" value="course_id">
<input type="text" name="course.name" value="course_name">
<input type="submit" value="提交">
</form>
Controller:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-465882.html
@Controller
@RequestMapping("/classtest")
public class TestController {
// 綁定變量名字和屬性,參數(shù)封裝進(jìn)類
@InitBinder("student")
public void initBinderUser(WebDataBinder binder) {
// 這里的”.”千萬(wàn)別忘記了
// 表示去掉前綴 student.
binder.setFieldDefaultPrefix("student.");
}
// 綁定變量名字和屬性,參數(shù)封裝進(jìn)類
@InitBinder("course")
public void initBinderAddr(WebDataBinder binder) {
binder.setFieldDefaultPrefix("course.");
}
@RequestMapping("/methodtest")
@ResponseBody
public Map<String,Object> test(Student student,@ModelAttribute("course") Course course){
Map<String,Object> map=new HashMap<String,Object>();
map.put("student", student);
map.put("course", course);
return map;
}
@InitBinder() 中間的value值,用于指定表單屬性或請(qǐng)求參數(shù)的名字,符合該名字的將使用此處的DataBinder。比如:student.id和student.note。student就得是中間的value值,這樣才能接收得到。而且student會(huì)填充進(jìn)WebDataBinder,這里binder對(duì)象就是student了。也可以用@ModelAttribute("student")
做限定。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-465882.html
到了這里,關(guān)于注解@RestControllerAdvice用法途的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!