background

We often encounter a variety of system exceptions in daily development, but it is very unfriendly to directly return the stack of exceptions to the page. Therefore, in our daily project development, I have summed up a good global Exception handling scheme which I think is a good volunteer reference, welcome your comments and support.

The basic idea

This approach mainly uses depth-first recursive loops, policy patterns, and enumeration-based singletons to implement exception handling logic individually for each different type of exception, which is mostly true in practice.

When an exception is thrown, there are two cases: one is no child exception (getCause() == NULL), the other is child exception. Depth-first means that a NullPointerException is thrown from within the project, and then the outer catch block is repackaged as IllegalArgumentException. Then NPE is the “source of the exception”. The end result is processing the NPE and returning it to the front end.

The policy mode simply registers the processing logic for each exception separately, puts it into the HashMap, and then returns it with getClass().

The specific implementation

  1. So let’s define aException handlerThe interface is used to mark processingException:
public interface ExceptionMessageConverter<T extends Throwable> {
    /** * There is no requirement for the return value type * Return ModelAndView or normal POJO * SpringMVC has a full processing mechanism, return JSON or View */
    Object onThrow( HttpServletRequest request, HttpServletResponse response, T exception );
}
Copy the code
  1. Exception handler registry:ExceptionRegistry
public final class ExceptionRegistry {
    // This is written when the project starts, there is no need to use ConcurrentHashMap
    final statis Map<Class, ExceptionMessageConverter> cached
        = new HashMap<>();
    
    public static void registry( Class
        type, ExceptionMessageConverter converter ) { cached.put(type, converter); }
    
    public static ExceptionMessageConverter get(Class type){
        returncached.get(type); }}Copy the code
  1. Predefined exception handlers, where enumeration-based singleton patterns are used
public enum PredefinitionExceptionConverterEnum 
    implements ExceptionMessageConverter {
    
    onBindException(BindException.class) {
        @Override
        public Object onThrow( HttpServletRequest request, HttpServletResponse response, Throwable exception ) {
            / /... Custom processing logic
            return new ResponseEntity("Length at least 6", HttpStatus.BAD_REQUEST);
        }
    },
    onConstraintViolationException(ConstraintViolationException.class) {
        // omit implementation...
    },
    / *... Customize more processing logic */
    ;
    
    PredefinitionExceptionConverterEnum(Class type) {
        // Register ExceptionRegistry in the constructor
        ExceptionRegistry.registry(type, this); }}Copy the code
  1. Exception handler: GlobalExceptionAdvice
@RestControllerAdvice
public class GlobalExceptionAdvice {
    
    public static Object doThrowable( HttpServletRequest request, HttpServletResponse response, Throwable root, Throwable ex ) {
        Throwable current = ex, cause;
        ExceptionMessageConverter converter = null;
        if((cause = current.getCause()) ! =null) {
            // If there is a sub-exception, the sub-exception takes precedence
            Object result = onThrowable(request, response, root, cause);
            if(result ! =null) {
                return result
            }
        }
        if((converter = ExceptionRegistry.get(current.getClass())) ! =null) {
            // If the child exception does not have a handler, the current exception is handled
            return handler.onThrowable(request, response, current);
        }
        return null;
    }
    
    /** * Catch the exception and pass it to doThrowable to handle the exception * Note that the return value here is ResponseEntity, * this is our project implementation, similar effect@ResponseBodyThe content-type returned is Application /json */
    @ExceptionHandler(Throwable.class)
    public ResponseEntity onThrowable( HttpServletRequest, request, HttpServletResponse response, Throwable ex ) throws Throwable {
        Object result = doThrowable(ex, ex);
        if (result == null) {
            // If there is no matching processor, go this way
            return new ResponseEntity("Internal system exception",
                HttpStauts.INTERNAL_SERVER_ERROR);
        }
        returnresult; }}Copy the code

Afterword.

At this point, a custom global exception handling is complete, but there are a few things you’d like to note

  • ExceptionRegistry and all methods are defined aspublicIn order to inPredefinitionExceptionConverterEnumYou can still customize and register other processors.
  • Our project didn’t use what other projects usually defineResultBodyincludesdata,status,errorCodeAnd so on return the body, because we project as much as possible withhttpStatus code return, may be the project is not large enough, there is no need for other status code, in short, this is sufficient for the present, and in the future, if you want to expand the custom status code and the front end has scheduled a plan, the front and back end architecture level has reserved space for expansion.
  • The above list of all kinds of seemingly cow coax noun everybody do not care too much about ha, I just realize this processing strategy, this area summed up it, the error also please be corrected!!

List some common exception types:

  1. BindException: Passes when Spring MVC binds data@NotBlankForm validation errors caused by related annotations
  2. ConstraintViolationException: This is still a parameter validation error;
  3. MissingServletRequestParameterException: This is the request parameters missing some parameters;
  4. MethodArgumentNotValidException: This is by@RequestBodyand@ValidAnnotation validation error
  5. TransientObjectExceptionThis is:hibernateCascading saved exceptions (used in our projectjpa), other entities on which an entity depends should be persisted, otherwise the exception will be thrown.
  6. EntityNotFoundException: As the name implies, the entity does not exist, orjpaThe exception;
  7. SQLSyntaxErrorException: SQL syntax error;
  8. SQLIntegrityConstraintViolationException: foreign key association error;
  9. MySQLTransactionRollbackException: As the name suggests,MySQLTransaction rollback error;
  10. SQLException: I don’t explain
  11. MalformedURLException: The URL format is incorrect. This URL format is caused by a network request.