Where to Handle Exceptions in Spring Applications

I am still attending Spring. I have a suite of apps installed and am trying to understand exception handling in Spring.

I am using @ControllerAdvice

exception handling. There are several layers in my application, such as Services

, Controllers

, Models

and Repositories

. In which layer should I handle my exceptions? Or should I handle the exception in each layer as needed?

+4


source to share


6 answers


This is a good way to trigger exception handling in Spring:

Step 1 - Create a specific DefaultExceptionHandler class and annotate it with @ControllerAdvice annotation . In this handler class, you use different methods, catching both expected and unexpected exceptions, which are annotated with the @ExceptionHandler annotation :

@ControllerAdvice("com.stackoverflow.example")
@SuppressWarnings("WeakerAccess")
public class DefaultExceptionHandler extends ResponseEntityExceptionHandler {

    private final Logger log = LoggerFactory.getLogger("DefaultExceptionHandler");

    private final MessageSourceAccessor messageSource;

    @Autowired
    public DefaultExceptionHandler(MessageSourceAccessor messageSource) {
        Assert.notNull(messageSource, "messageSource must not be null");
        this.messageSource = messageSource;
     }

      @ExceptionHandler(ApplicationSpecificException.class)
      public ResponseEntity<Object> handleApplicationSpecificException(ApplicationSpecificExceptionex) {
         final Error error = buildError(ex);
         return handleExceptionInternal(ex, ex.getHttpStatus(), error);
      }

       @ExceptionHandler(Exception.class)
       public ResponseEntity<Object> handleException(Exception ex) {
           final Error error = buildError(ex);
           return handleExceptionInternal(ex, HttpStatus.INTERNAL_SERVER_ERROR, error);
    }
}

      

Step 2 - Throw an application specific exception (ApplicationSpecificException class) used for expected exceptions and throw that exception at any level and it will be picked up by Spring:

public class ApplicationSpecificException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private final ExceptionType exceptionType;

    public ApplicationSpecificException(ExceptionType exceptionType, Object... messageArguments) {
        super(MessageFormat.format(exceptionType.getMessage(), messageArguments));
        this.exceptionType = exceptionType;
    }

    public ApplicationSpecificException(ExceptionType exceptionType, final Throwable cause, Object... messageArguments) {
        super(MessageFormat.format(exceptionType.getMessage(), messageArguments), cause);
        this.exceptionType = exceptionType;
    }

    public HttpStatus getHttpStatus() {
        return exceptionType.getStatus();
    }

    public ExceptionType getExceptionType() {
        return exceptionType;
    }
}

      

With ExceptionType there will be an enum:



public enum ExceptionType {

    HTTP_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "An internal server error occurred.");
    //you can specify your own exception types...

    private HttpStatus status;
    private String message;

    ExceptionType(HttpStatus status, String message) {
        this.status = status;
        this.message = message;
    }

    public HttpStatus getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }
}

      

Step 3 - Finally, the ExceptionFactory class is created. This allows the exception to be automatically logged in the application logs:

public class ExceptionFactory {

    private static final Logger LOG = LoggerFactory.getLogger(ExceptionFactory.class);

    public static ApplicationSpecificException create(final Throwable cause, final ExceptionType exceptionType, final Object... messageArguments) {
        LOG.error(MessageFormat.format(exceptionType.getMessage(), messageArguments), cause);
        return new ApplicationSpecificException (exceptionType, cause, messageArguments);
    }

    public static ApplicationSpecificException create(final ExceptionType exceptionType, final Object... messageArguments) {
        LOG.error(MessageFormat.format(exceptionType.getMessage(), messageArguments));
        return new TerminologyServerException(exceptionType, messageArguments);
    }
}

      

Step 4 - Anywhere in the application you can now throw an exception and this will log the exception in the application logs. This exception is thrown and given the DefaultExceptionHandler value thanks to Spring's @ControllerAdvice annotation:

throw ExceptionFactory.create(ExceptionType.INTERNAL_SERVER_ERROR);

      

Likewise, you deal with the exception handling process as a cross-cutting issue. Internal server errors will not be propagated to the end user, and both expected and unexpected exceptions are handled by the DefaultExceptionHandler function. The exception is assigned a specific HTTP error code and error message that will be returned to the client.

+5


source


It is good practice to annotate the allocated class with a help @ControllerAdvice

that handles any unexpected problems . By doing so, you are not exposing the client to internal documents.

@ControllerAdvice
public class UncaughtExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(UncaughtExceptionHandler.class);

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public void handleAll(Exception e) {
        log.error("Unhandled exception occurred", e);
    }

}

      



For expected exceptions (not to be confused with checked exceptions) you should probably be dealing with this issue where it happens. Some exceptions can be propagated or wrapped and returned in the same global handler, implemented as @ControllerAdvice

so that all logic is allocated for exceptions in one place.

+3


source


You should try annotation @ExceptionHandler

.

You can read more about this here: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

+2


source


You can use @ControllerAdvice

as a global exception handler in your spring application. Checkout this well explained tutorial. Using @ControllerAdvice

makes your business code less cluttered and then you have a separate place where you can handle all your exceptions. @ControllerAdvice

enforces separation of concerns by design .

+2


source


There are three ways to handle an exception using the Spring Framework.

  1. @ExceptionHandler - Controller based

    This handler is controller based, we need to have a method annotated with @ExceptionHandler that takes as an argument the exception class [any exception you want to handle], if any of these exceptions are thrown in the controller, then this handler method will handle.

    If we have two handler methods in one controller, say, for example, one handler for Exception and another handler for RuntimeException, then the handler method that is closer to the hierarchy of the Exception class is triggered. In this case, a NullpointerException is thrown, and then the IOException handler method that is closest to the Exception class is run.

  2. @ControllerAdvice - Global exception handler

    This is used for global error handling in Spring application. All you need is a class annotated with @ControllerAdvice. If any exception occurs in a particular controller [you can define to which packages this controller tip should listen for the exception in base packages], then it is handled by the ControllerAdvice.

    You will have multiple @ExceptionHandlers in the ControllerAdvice as shown in the snippet below. This class can be the only place where you can handle exceptions for the entire application.

    @ControllerAdvice(basePackages = "{com.exampe.controller}")
    public class RestApiExceptionHandlerAdvice {
    
        /** Handling Business exception */  
    
        @ExceptionHandler(value = BadRequestException.class)
        public ErrorMessage handleBadRequest(BadRequestException exception) {
            //code...
            return errMsg;
        }
    
        @ExceptionHandler(value = GatewayTimeoutException.class)
        public ErrorMessage handleGatewayTimeout(GatewayTimeoutException exception) {
            //code...
            return errMsg;
        }
    
    }
    
          

  3. HandlerExceptionResolver

    With the above two methods, in most cases we use static pages. In this case, we can revert different views to different exceptions. we can customize using MySimpleMappingExceptionResolver in spring.xml and you can specify which view should be displayed for which exception.

+1


source


I think the accepted answer is good. It uses Spring @ControllerAdvice

for global exception handling as well as enumeration to combine error code and HTTP status.

However, I disagree with the factory part of Exception. The factory registers the exception and throws it. It is discouraged in many articles like this and this one . Of course, unless you have any kind of exception, for example, everything should be fine, since the exception will only be logged once. But you just couldn't be sure that the translation won't be needed in the future.

I would recommend using logging @ControllerAdvice

as this is the final destination for your application exclusion. This can ensure that each exception is logged once and has a good descriptor to present to the client.

0


source







All Articles