Java – the recommended way to handle exception in Spring MVC

javaspring-mvc

I have a Spring application, where we make few service calls to fetch data. There is a Data Layer in between the Controller and the Service layers.

Controller (Request-Mapping) -> Data Layer -> Service Layer

If we do not get the data from the underlying service, we intend to throw an exception. My question is regarding the best practice to adopt here:

  1. Should I make the DataProvider (data layer) and the controller
    throw the exception and let the tomcat handle it via web.xml?

  2. Should I catch the exception in Data Layer itself, log it and not propagate the exception to the Controller?

Specifically, would it be a good thing mark the controller as "throws CustomException" ?

Best Answer

Contrary to the opinion that throwing exceptions from Controller handler methods is bad, I do recommend to do so. The reasons are following:

  1. It eliminates a lot of boilerplate code. Almost all try-catches in controllers are the same and are frequently copy-pasted from one place to another. It increases code maintenance burden and coupling between controllers and exception hierarchy.

  2. There are other ways to handle exceptions correctly. Best solution is to have a global exception handler, which translates specific types of exceptions to HTTP response codes. E.g. when your database query or remote call fails with timeout, you can translate it to HTTP 503. When your repository throws ObjectNotFoundException, you can return HTTP 404. When validation fails, respond with HTTP 400. You can also have controller-specific handlers in few specific cases (vary rare situtation).

That said, it does not mean, that you should have "throws Exception" in method signature. In layered architecture objects in higher layers like controllers cannot expect low-level exceptions (e.g. from JDBC driver), because it would violate encapsulation. There should be separate exception hierarchy per layer, which has semantics specific for that layer, and there should be exception translation (normally done by wrapping). For example, ConstraintViolationException in Repository may result in InvalidServiceRequestException in Service and translated to HTTP 400 by exception handler.