Classes with the @Controller annotation and @ControllerAdvice can have methods marked with the @ExceptionHandler annotation to handle exceptions from controller methods, as shown in the following example:

Java

@Controller
public class SimpleController {
    // ...
    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {
        // ...
    }
}
Kotlin

@Controller
class SimpleController {
    // ...
    @ExceptionHandler
    fun handle(ex: IOException): ResponseEntity<String> {
        // ...
    }
}

The exception can be a propagated high-level exception (for example, a directly thrown IOException) or a nested cause inside a wrapper exception (for example, IOException wrapped inside an IllegalStateException). As of 5.3, this can match at arbitrary cause levels, whereas previously only the immediate cause was taken into account.

For matching exception types, it is preferable to declare the target exception as a method argument, as shown in the previous example. When multiple exclusion methods match, a root exclusion match is generally preferred over a cause exclusion match. In particular, the ExceptionDepthComparator is used to sort exceptions based on their depth of the type of exception thrown.

Additionally, an annotation declaration can narrow down the types of exceptions that must match, as shown in the following example :

Java

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
    // ...
}
Kotlin

@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: IOException): ResponseEntity<String> {
    // ...
}

You can even use a list of specific exception types with a very general argument signature, as shown in the following example:

Java

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
    // ...
}
Kotlin

@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: Exception): ResponseEntity<String> {
    // ...
}

The difference between root and cause exception mapping can surprise.

In the IOException variant shown earlier, the method is typically called using the actual FileSystemException or RemoteException instance as an argument, since both of them extend from IOException. However, if any such matching exception is propagated within a wrapper exception that is itself an IOException, then the passed exception instance is that wrapper exception.

In the handle(Exception)option the operating logic is even simpler. It is called using a wrapper exception in the wrapping script, while the actual corresponding exception can be found via ex.getCause() in this case. The exception passed is an actual instance of FileSystemException or RemoteException only if they are thrown as high-level exceptions.

It is generally recommended to give as many be more specific in the argument signature to reduce the likelihood of inconsistency between root and cause exception types. Consider the possibility of breaking a multi-hit method into separate methods annotated with @ExceptionHandler, each of which matches one specific type of exception through its signature.

In a configuration with multiple @ControllerAdvice annotations we recommend declaring root exception mappings in the @ControllerAdvice annotation in appropriate priority order. Although root exception matching is preferred over cause exception matching, the definition occurs among the methods of a given controller or class annotated with @ControllerAdvice. This means that a cause match for a bean annotated with @ControllerAdvice with a higher priority is preferred over any match (such as root) for a bean annotated with @ControllerAdvice with a lower priority.

Last but not least, an implementation of a method marked with the @ExceptionHandler annotation may refuse to handle a given exception instance by re-throwing it in its original form. This is useful in cases where you are only interested in matches at the root level, or matches in a specific context that cannot be determined statically. Re-throwing of the exception propagates along the remaining resolution chain as if the given method with the @ExceptionHandler annotation did not match the first one.

Support for methods with the @ExceptionHandler annotation in Spring MVC it is built at the DispatcherServlet level, the HandlerExceptionResolver mechanism.

Method arguments

Methods with the @ExceptionHandler annotation support the following arguments:

Method argument Description

Exception type

Provides access to the exception thrown.

HandlerMethod

Provides access to the method controller that threw the exception.

WebRequest, NativeWebRequest

Typed access to request parameters and request and session attributes without direct use of the Servlet API.

javax. servlet.ServletRequest, javax.servlet.ServletResponse

Select any specific request or response type (for example, ServletRequest or HttpServletRequest, or MultipartRequest or MultipartHttpServletRequest from Spring).

javax.servlet.http.HttpSession

Ensures the presence of a session. As a consequence, such an argument is never null.
Note that session access is not thread safe. Consider setting the synchronizeOnSession flag of the RequestMappingHandlerAdapter instance to true if multiple requests are allowed to access the session simultaneously.

java.security.Principal

The current authenticated user – possibly a concrete implementation class of Principal, if known.

HttpMethod

HTTP request method.

java.util.Locale

The current locale of the request, determined by the most specific LocaleResolver available - essentially the configured LocaleResolver or LocaleContextResolver.

java.util.TimeZone, java.time.ZoneId

The time zone associated with the current request, as determined by the LocaleContextResolver.

java.io.OutputStream, java.io.Writer

Provides access to the raw response body, as this is provided by the Servlet API.

java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap

Provides access to the model to provide an error response. Always empty.

RedirectAttributes

Set the attributes used in the case of a redirect - (which will be added to the request string) and flash attributes for temporary storage until the request arrives after the redirect.

@SessionAttribute

Designed to provide access to any session attribute, as opposed to model attributes, stored in the session as a result of declaring the @SessionAttributes annotation on class level.

@RequestAttribute

Designed to provide access to request attributes.

Return values

Methods with the @ExceptionHandler annotation support the following return values:

Return value Description

@ResponseBody

The return value is converted through HttpMessageConverter instances and written in the response.

HttpEntity<B>, ResponseEntity<B>

The return value specifies that the full response (including HTTP headers and body) will be converted through HttpMessageConverter instances and written into the response.

String

The name of the view that should be recognized by ViewResolver implementations and used in conjunction with an implicit model - defined through command objects and methods with the @ModelAttribute annotation. The handler method can also programmatically modify the model by declaring a Model argument (described earlier).

View

A View instance intended to be rendered along with an implicit model - defined through command objects and methods with the annotation @ModelAttribute. A handler method can also programmatically modify a model by declaring a Model argument (described earlier).

java.util .Map, org.springframework.ui.Model

Attributes to add to the implicit model with the view name implicitly defined via RequestToViewNameTranslator.

@ModelAttribute

An attribute added to the model with the view name implicitly defined via RequestToViewNameTranslator.

Note that the @ModelAttribute annotation is optional. See the "Any Other Return Value" section at the end of this table.

ModelAndView object

The view and model attributes to use and, if necessary, the response status.

void

A method with a return type void (or a return value null) is considered to have fully processed the response if it also has the ServletResponse or OutputStream argument, or the @ResponseStatus annotation. The same is true if the controller has performed a positive check of the ETag or the lastModified timestamp.

If neither of the above values is true, the return type is void can also indicate "no response body" for REST controllers or a default view name choice for HTML controllers.

Any other return value

If the return value does not match any of the above and is not a prime type (as defined by BeanUtils#isSimpleProperty), by by default it is considered a model attribute that must be added to the model. If it is a simple type, then it remains unresolved.

Dealing with exceptions in the REST API

A common requirement for services REST is to include error information in the response body. The Spring Framework does not automatically do this, since the presentation of error information in the response body is application-specific. However, the @RestController annotation can use methods marked with the @ExceptionHandler annotation with a ResponseEntity return value to set the response status and body. Such methods can also be declared in classes annotated with @ControllerAdvice so that they can be applied globally.

Applications that implement global exception handling with a detailed error description in the response body should consider extensible ResponseEntityExceptionHandler, which handles exceptions thrown in Spring MVC and provides interceptors for customizing the response body. To take advantage of this, create a subclass of ResponseEntityExceptionHandler , annotate it with @ControllerAdvice, override the necessary methods and declare it as a Spring bean.