Enumeration class defines exception type and usage of annotation @RestControllerAdvice

https://blog.csdn.net/m0_51620667/article/details/127013347

1. What is @RestControllerAdvice
@RestControllerAdvice is a combined annotation consisting of @ControllerAdvice and @ResponseBody, and @ControllerAdvice inherits @Component, so @RestControllerAdvice is essentially a Component for defining @ExceptionHandler, @InitBinder and @ModelAttribute methods, Applies to all methods using @RequestMapping.

2. Features of @RestControllerAdvice
Through the @ControllerAdvice annotation, the global configuration of the controller can be placed in the same position.
Methods of classes annotated with @RestControllerAdvice can be annotated to methods using @ExceptionHandler, @InitBinder, @ModelAttribute.
The @RestControllerAdvice annotation will act on all controller methods annotated with @RequestMapping.
@ExceptionHandler: Used to specify the exception handling method. When used in conjunction with @RestControllerAdvice, it is used to globally handle exceptions in the controller.
@InitBinder: Used to set WebDataBinder to automatically bind front-end request parameters to Model.
@ModelAttribute: The original function is to bind the key-value pair to the Model. When used in conjunction with @ControllerAdvice, the global @RequestMapping can obtain the key-value pair set here
@ControllerAdvice  
public class GlobalController{  
     
    //(1 ) Global data binding
    //applied to all @RequestMapping annotation methods  
    //Add key-value pairs to the global here, and methods annotated with @RequestMapping can get this key-value pair  
    @ModelAttribute 
    public void addUser(Model model) {   
        model.addAttribute("msg", "The key-value pair is added to the global here, and the method annotated with @RequestMapping can obtain this key-value pair"); }  
    //    
    (2) Global data preprocessing
    //applied to all @RequestMapping annotation method, initialize the data binder before its execution  
    //Used to set WebDataBinder  
    @InitBinder("user")
    public void initBinder(WebDataBinder binder) {     }         // (3) Global exception handling     //Apply to all @ The method of the RequestMapping annotation is executed when an Exception is thrown       //Defining global exception handling, the value attribute can filter and intercept specified exceptions, here intercept all Exception       @ExceptionHandler(Exception.class)         public String handleException(Exception e) {             return "error";     }     }  

    








@ControllerAdvice can specify the Controller scope

basePackages: Specify one or more packages, all Controllers under these packages and their subpackages are managed by this @ControllerAdvice
@RestControllerAdvice(basePackages={"top.onething"})
@Slf4j
public class ExceptionHandlerAdvice {    
    @ExceptionHandler(Exception.class )    
    public String handleException(Exception e) {    
        return "error";
    }   


basePackageClasses: It is a variant of basePackages, specifying one or more Controller classes. ControllerAdvice management
@RestControllerAdvice(basePackageClasses={TestController.class})
@Slf4j
public class ExceptionHandlerAdvice {     @ExceptionHandler(Exception.class)         public String handleException(Exception e) {             return "error";



    } 
}  
1
2
3 4
5
6
7 8 assignableTypes: Specify one or more Controller classes that are managed by this @ControllerAdvice
@RestControllerAdvice (assignableTypes={TestController.class}) @Slf4j public class ExceptionHandlerAdvice {     @ExceptionHandler(Exception.class )         public String handleException (Exception e)     { return   "     error          " ; Slf4j public class ExceptionHandlerAdvice {















@ExceptionHandler     (Exception.class)    
    public String handleException (Exception e) {     return  "
        error"
    ; 



First, you need to design a custom exception class for your own system, and pass the status code through it.

/** 
 * Custom exception
 */
public class SystemException extends RuntimeException{     private String code;//status code     public SystemException(String message, String code) {         super(message);         this.code = code;     }     public String getCode() {         return code;     } } The first idea is to design a base class










/**
 * The class that handles the exception, the Controller that needs to handle the exception directly inherits this class
 */
public class BaseController {     /**      * Handles the exception thrown by the Controller      * @param e exception instance      * @return The return value of the Controller layer      */     @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("System error");
        }
    }
}

Afterwards, all Controllers that need exception handling inherit this class, so as to obtain the method of exception handling.
Although this method can solve the problem, it is extremely inflexible, because the inheritance mechanism is used only to obtain a default method, which is obviously not good.

The second way of thinking is to turn this base class into an interface and provide the default implementation of this method (that is, the default method in the interface, java8 began to support the default implementation of the interface method)

/**
 * Exception handling in the form of interface
 */
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(" System Error");         }     }















}

Although this method does not occupy inheritance, it is not very elegant, because almost all Controllers need to handle exceptions, so I need to write implement DataExceptionSolver for each Controller, which is obviously not what I really want. Moreover, this method relies on the syntax unique to java8, which is a big limitation.

The third idea is to use enhanced Controller for global exception handling.

Come to a case

1. Define an exception information description basic information interface class

public interface BaseErrorInfoInterface {     /** error code*/      String getResultCode();

    /** Error description*/
     String getResultMsg();
}

2. Define an enumeration class to implement the above exception information description interface

public enum CommonEnum implements BaseErrorInfoInterface {     // Data operation error definition     SUCCESS("200", "Success!"),      BODY_NOT_MATCH("400", "The requested data format does not match!"),     SIGNATURE_NOT_MATCH("401", "The requested number Signature mismatch!"),     NOT_FOUND("404", "The resource was not found!"),      INTERNAL_SERVER_ERROR("500", "Internal server error!"),     SERVER_BUSY("503", "The server is busy, please wait Try again!");






    /** Error code*/
    private String resultCode;

    /** Error description*/
    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. Define a custom exception class to identify the exception information that occurs in the business system

public class BizException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    /**
     * Error code
     */
    protected String errorCode;
    /**
     * Error message
     */
    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. Define a unified result return data encapsulation class

public class ResultBody {     /**      * response code      */     private String code;



    /**
     * Response message
     */
    private String message;

    /**
     * Response result
     */
    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. Define a global exception handling class

@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * Handle custom business exceptions
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = BizException.class)  
    @ResponseBody  
    public ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){         logger.error(" A business exception occurred! The reason is: {}",e.getErrorMsg());         return ResultBody.error(e.getErrorCode(),e.getErrorMsg());     }


    /**
     * Handle null pointer exceptions
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    @ResponseBody
    public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){         logger.error("happens Null pointer exception! Reason: ",e);         return ResultBody.error(CommonEnum.BODY_NOT_MATCH);     }



    /**
     * Handle other exceptions
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =Exception.class)
    @ResponseBody
    public ResultBody exceptionHandler(HttpServletRequest req, Exception e){         logger.error("Unknown exception! The reason is: ",e);         return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);     } }




Explanation: The above code uses @ControllerAdvice and @ExceptionHandler annotations. The role of @ControllerAdvice is to enable the capture of global exceptions. This annotation can also specify a specific Controller class through the assignableTypes parameter, so that the exception handling class only handles exceptions thrown by a specific class. The @ExceptionHandler annotation indicates the exception type handled by the processing method body.

4. @InitBinder
SpringMVC is not able to bind all types of parameters. If you bind Date type parameters, an IllegalStateException error will be reported. So you need to register some type binders for binding parameters. The InitBinder annotation has this effect.

    @InitBinder
    public void dateTypeBinder(WebDataBinder webDataBinder){         //Add a DateFormatter date converter to the data binder.         webDataBinder.addCustomFormatter(new DateFormatter("yyyy-mm-dd"));     } The binder registered with @InitBinder is only valid in the current Controller and will not act on other Controllers.




If you think it is too complicated to write in each Controller, you can write a BaseController and let other Controllers inherit this class

We can customize the format converter and implement the Formatter interface. You can also add validators and more.

public class StringFormatter implements Formatter<String> {
    private static final String PREFIX = "prefix- ";

    @Override
    public String parse(String text, Locale locale) throws ParseException {         //So String type parameters are all prefixed.         String result = PREFIX + text;         return result;     }



    @Override
    public String print(String object, Locale locale) {         return object;     } } then added to the data binder




    @InitBinder
    public void dateTypeBinder(WebDataBinder webDataBinder){         //Add a DateFormatter date converter to the data binder.         webDataBinder.addCustomFormatter(new DateFormatter("yyyy-mm-dd"));         // Add a String type data binder to add a prefix         webDataBinder.addCustomFormatter(new StringFormatter());     } Of ​​course, you can also make your own Register a custom editor






The custom editor class needs to inherit org.springframework.beans.propertyeditors.PropertiesEditor; and rewrite its setAsText and getAsText methods

Then just register in the InitBinder method.

Here is a demo for everyone

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();    
        }    
    }  

}


Using @InitBinder to implement form multi-object delivery tips

Student object and Course object:

public class Student implements Serializable{  
  String id;  
  String note;  
  //get..set....  
}  
public class Course implements Serializable{  
  String id;  
  String note;
  //set..get...  
}  

HTML页面:

<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:

@Controller  
@RequestMapping("/classtest")  
public class TestController {  
    // Bind variable names and properties, parameters are encapsulated into classes  
    @InitBinder("student")  
    public void initBinderUser(WebDataBinder binder) {  
        // "." here Don't forget
        // means to remove the prefix student.binder.setFieldDefaultPrefix
        ("student.");  
    }  
    // Bind variable names and properties, parameters are encapsulated into classes  
    @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() The intermediate value value is used to specify the name of the form attribute or request parameter, which matches the name will use the DataBinder here. For example: student.id and student.note. student must be the middle value, so that it can be received. And the student will be filled into the WebDataBinder, where the binder object is the student. It can also be limited with @ModelAttribute("student").

Guess you like

Origin blog.csdn.net/Maxiao1204/article/details/129496346