1. Introduction
I believe that every one of us in SpringMVC development has encountered such a problem: when our code works properly, the data is returned in the format we expect, such as JSON or XML, but once an exception occurs (such as: NPE, array out of bounds, etc.), the returned content is the server exception stack information, resulting in the returned data can not be properly parsed by the client; Obviously, these are not the results we were hoping for.
As we know, a common system will involve control layer, service (business) layer, cache layer, storage layer and interface invocation, etc., and each link of which will inevitably encounter various unpredictable exceptions to be dealt with. If each step is separately try.. Catch will make the system very messy, poor readability, high maintenance costs; The common way is to realize unified exception handling, so that all kinds of exceptions are decoupled from each module.
2. Handle common global exceptions
There are three main types of global exception handling in Spring:
(1) Annotate ExceptionHandler
(2) Inherits HandlerExceptionResolver interface
(3) Annotate ControllerAdvice
For example, HTTP error code 400 (request invalid) and 500 (internal server error), look at the test code and return results without any processing.
Figure 1: Test code
Figure 2: Error returns with no exceptions
2.1 annotations ExceptionHandler
The simplest way to use the ExceptionHandler annotation is to place it in the controller file. The detailed annotation definition will not be introduced. If there are multiple controller files in the project, it is usually possible to implement ExceptionHandler exception handling in baseController, and each Contoller inherits baseController to achieve the purpose of unified exception handling. Because it is common, the simple code is as follows:
Figure 3: ExceptionHandler use in Controller
In the return of the exception, added to the class name, easy to understand the memory. Run to see the result:
Figure 4: The result after adding ExceptionHandler
-
Advantages: ExceptionHandler is simple to understand, and there is no qualified method format for exception handling;
-
Disadvantages: Since ExceptionHandler only applies to methods, in the case of multiple controllers, all controllers that need exception handling inherit from this class for only one method. It is not good to force a parent for unrelated things.
2.2 annotations ControllerAdvice
This is a combination of the ControllerAdvice annotation and ExceptionHandler. As you can see above, when using @ExceptionHandler alone, it must be in a Controller, whereas when used in combination with ControllerAdvice, this restriction is completely removed. In other words, the combination of the two achieves global exception catching handling.
Figure 5: Annotating the ControllerAdvice exception handling code
Before running, we need to comment out ExceptionHandler in the previous Controller, and the test results are as follows:
Figure 6: Annotating the result of the ControllerAdvice exception handling
As you can see from the above results, the exception handling has indeed changed to the ExceptionHandlerAdvice class. This method integrates all exception handling into one place, removes the inheritance relationship in Controller, and achieves the effect of global capture. It is recommended to use this method.
2.3 Implementing the HandlerExceptionResolver interface
HandlerExceptionResolver is an internal interface of SpringMVC, which has only one method, resolveException. By implementing this interface, we can achieve the purpose of global exception handling.
Figure 7: Implementing the HandlerExceptionResolver interface
Figure 8: The result of implementing the HandlerExceptionResolver interface
You can see that the exception handling for 500 is in effect, but the exception handling for 400 is not, and the result is the same without the root exception. What’s going on? You can do global exception handling, right? There is no way to know the cause of the problem, we can only dig to the root, to Spring’s ancestral grave, we combined with Spring source debugging, to need the reason.
3. Analysis of Spring exception handling source code
As you all know, the first class to receive a request in Spring is DispatcherServlet, and the core method in that class is doDispatch, where you can break points and follow up on exception handling.
3.1 HandlerExceptionResolver Implements the processing process of the class
Break processHandlerException by following the following follow up steps. The result of the trace is as follows:
Figure 9: processHandlerException breakpoint
Can see the arrow in the picture [1], the traverse handlerExceptionResolvers, in turn, to handle exceptions, and in the arrow [2], there’re a total of four elements, see handlerExceptionResolvers one last is 2.3 method definition exception handling classes
The current request is query request. According to the above phenomenon, it can be inferred that the exception handling should be handled in the first three exception handling, so as to skip our custom exception; With speculation, we F8 to continue to follow up, can trace to the exception is the third, namely DefaultHandlerExceptionResolver being processed.
-
DefaultHandlerExceptionResolver: Default assembly for SpringMVC DefaultHandlerExceptionResolver, the class of doResolveException method mainly for some special exception handling, and converts such exceptions to the corresponding response status code. And query request to trigger the exception for MissingServletRequestParameterException, its also by DefaultHandlerExceptionResolver for exception, it is caught exception in the class.
To the truth, you can see our custom class MyHandlerExceptionResolver can really achieve global handle exceptions, but for the query request exceptions, intermediate was stuck a foot DefaultHandlerExceptionResolver, So I skipped MyHandlerExceptionResolver class processing, thus appear 400 returns the result. For the CALC request, there is no block in the middle, so the expected effect is achieved.
3.2 Processing sequence of three types of exceptions
Here we introduces three kinds of the global total of exception handling, according to the above analysis we can see that implement way is at the bottom of HandlerExceptionResolver interface, then @ ExceptionHandler and @ ControllerAdvice after the order of the two who first who? Turn on all three types of exception handling (previously commented out) and run them to see what happens:
Figure 10: The result of an exception handling release
We can see that @ExceptionHandle is the first exception handling in Controller, and @ControllerAdvice is the second exception handling in Controller. We can write a Controller02, copy query and calc, and leave out the exception handling, so that when we request c02’s methods, we catch exceptions in the same class as @controllerAdvice.
Above are our conclusions based on the phenomenon, the following Spring source code to find “evidence”. In figure 9, have 4 class in handlerExceptionResolvers processor, While @ ExceptionHandler and @ ControllerAdvice treatment in the first ExceptionHandlerExceptionResolver (before the breakpoint follow up can get). Continue to follow up until into ExceptionHandlerExceptionResolver doResolveHandlerMethodException method of a class, HandlerMethod is the method that Spring maps the HTTP request to the specified Controller, and Exception is the Exception that needs to be caught. Follow up and see what happens with these two parameters.
Figure 11: doResolveHandlerMethodException breakpoints
Continue to follow up getExceptionHandlerMethod method, found that there are two variables may be the key to the problem: exceptionHandlerCache and exceptionHandlerAdviceCache. First, the variable names of both are questionable; Second, in code, the first class is clearly used as the key to get a resolver, which matches the @ExceptionHandler rule in Controller. Finally, the processing order of the two Cache is also consistent with the previous conclusion. As you guessed before, Spring does look for the corresponding ExceptionHandler based on the Controller class name first. If it is not found, @controllerAdvice handles the exception.
Figure 12: Two exception handling caches
If you are interested can continue to dig deeper into the Spring source, here for ExceptionHandlerExceptionResolver simple summarize:
-
ExceptionHandlerCache contains ExceptionHandler exception handling in Controller. During processing, the Controller is obtained by HandlerMethod, and then the exception handling method is found. It should be noted that, It is put value during exception handling;
-
ExceptionHandlerAdviceCache is initialized in the project startup, general idea is to find a @ ControllerAdvice annotations of bean, the ExceptionHandler to cache the bean, when exception handling need to align traversal search process, And then achieve the purpose of global processing.
3.3 Turn the fish around
So with all of that, let me just draw a quick picture. The blue parts are the three types of exception handlers Spring adds by default, and the yellow parts are the exception handlers we added and where and in what order they were called. Look at where there are not too clear, flip looked back (ResponseStatusExceptionResolver for @ ResponseStatus annotations, here no longer expatiatory).
Figure 13: Exception summary
If has the need to advance the MyHandlerExceptionResolver, even before ExceptionHandlerExceptionResolver, can do it? The answer is yes, if you want to advance the MyHandlerExceptionResolver exception handling in the Spring, the need to implement a Ordered interface, realize the inside of the getOrder method can, return 1 here, put it in the top, salted fish can finally turn this time.
Figure 14: Implementing the Ordered interface
Run it to see if it works as expected. Just a reminder, all three of our exception handling works, as shown below:
Figure 15: Implementing the Ordered interface run result
4. To summarize
This article mainly introduces three kinds of common global exception handling in SpringMVC, found the problem in debugging, and then triggered to Spring source code to explore the cause, and finally solve the problem, I hope you can gain something. Of course, Spring exception handling classes more than these, interested in children’s shoes please explore!
Reference links:
[1] http://www.cnblogs.com/fangjian0423/p/springMVC-request-mapping.html
[2] blog.csdn.net/mll999888/a…
Creditease Institute of Technology