Videos
I've got a little problem with your exception handling. Principally it is absolutely ok to catch runtime exceptions, handle them and send them forth to the client, which is probably someone using your REST service and getting the error response as a JSON object. If you manage to tell him what he did wrong and what he can do about it, great! Of course, it will add some complexity to it, but it is probably easy and comfortable to work with that API.
But think about the backend developers, too, that work with your code. Especially the public User findById(Long id) method in your UserService is obscure. The reason for this is that you made your BusinessException, in particular, the UserNotFoundException unchecked.
If I joined your (backend) team, and I was to write some business logic using that service, I'd be quite sure what I had to expect from that method: I pass a user ID and get back a User object if it was found or null if not. That's why I would write code like that
User user = userService.findById("42A");
if (user == null) {
// create a User or return an error or null or whatever
} else {
// proceed
}
However, I would never know, that the first condition will never be true since you never return null. How should I know that I had to catch an Exception?
Is the compiler telling me to catch it? No, as it is not checked.
Would I look into your source code? Hell, no! Your case is extremely simple. That UserNotFoundException may be raised in another method in another class among hundred lines of code. Sometimes I couldn't look inside it, anyway, as that UserService is just a compiled class in a dependency.
Do I read the JavaDoc? Hahaha. Let's say, 50% of the time I wouldn't, and the other 50% you've forgotten to document it, anyway.
So, the developer has to wait until his code is used (either by a client or in Unit tests) to see that it doesn't work as he intended, forcing him to redesign what he has coded so far. And if your whole API is designed that way, that unchecked exceptions pop out of nowhere, it can be very very annoying, it costs time and money and is so easy to avoid, actually.
I use a similar way to handle exceptions. But in my case, different handlers are managed according to the error status (e.g. an user exists, an user cannot be registered due to some unsatisfied condition, etc.).
You also might add your generic BusinessException for some special cases. Hope it helps you feel better.
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.rest.restwebservices.controller.UserController;
import com.rest.restwebservices.exception.ResourceNotFoundException;
import com.rest.restwebservices.exception.PreconditionFailedException;
import com.rest.restwebservices.exception.ResourceAlreadyExistsException;
import com.rest.restwebservices.exception.fault.BusinessFault;
@ControllerAdvice(basePackageClasses = UserController.class)
public class BusinessExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseBody
public ResponseEntity<BusinessFault> genericHandler(HttpServletRequest request, ResourceNotFoundException ex) {
return new ResponseEntity<BusinessFault>(ex.getBusinessFault(), HttpStatus.NOT_FOUND);
}
@ExceptionHandler(PreconditionFailedException.class)
@ResponseBody
public ResponseEntity<BusinessFault> genericHandler(HttpServletRequest request, PreconditionFailedExceptionex) {
return new ResponseEntity<BusinessFault>(ex.getBusinessFault(), HttpStatus.PRECONDITION_FAILED);
}
@ExceptionHandler(ResourceAlreadyExistsException.class)
@ResponseBody
public ResponseEntity<BusinessFault> genericHandler(HttpServletRequest request, ResourceAlreadyExistsException) {
return new ResponseEntity<BusinessFault>(ex.getBusinessFault(), HttpStatus.CONFLICT);
}
}
I have to reduce the try-catch block from service classes in the Spring Boot Application. Is there any way? please suggest.
I generally avoid throwing exceptions manually because they're a bit expensive. I usually just return from the method as soon as possible.
But, for the exceptions that I can't escape, they're all handled in a class annotated with @RestControllerAdvice. Look it up and you might be able to avoid try/catch everywhere
In general, avoid using exceptions as part of your own control flow, handle exceptions from third party as early as possible (similar to an "anti-coruption-layer" you may throw domain specific exceptions for exception that you can't handle), propagate exceptions that can not be handled "normaly" to a general exception handler (as others already have suggested).
For example, you may catch third-party exceptions in an adapter to some external system and give some resonable response (ie an empty list or a null value). If the external system is vital for your app you may have have a probe that regulary validates the availabillity and switch over to an alternative solution if the other system is not available for some reason (it may just be that the user is prohibited from performing some operations in your app during this time).