Blog
Mowing repetitive code with an often forgotten annotation
I like code as long as it’s readable. So my resolution for the New Year, clean code. Easier said than done, and in most cases it requires a lot of preparation to end up with clean code. My priority is getting stuff to work and later on in the process I care about appearance. Functionality over beauty, right? Well, I learned it’s more efficient to do both at the same time. Recently I discovered some great tools to speed up the process for clean Java code.
For this post I will stick to exceptionhandling, but in my next posts the wonders of custom Builders will follow and maybe some of you know some kickass tools I could use to expand my toolbox. If so, feel free to leave them in the comment section.
Removing repetition
To handle an exception you would basically write a try/catch in the controller to inform the user of a problem the api has encountered. It works, but in case of many exceptions and when your software grows, it can get crowded in the controllers. The following code shows a basic implementation of exception handling. In case something goes wrong during the call for all messages, you’ll receive a response for which the Spring Framework already has a mechanism in place. You could use the standard message by returning e.getMessage(), as done below, or you could write your own message. The important part to notice is that in this approach you would have to write a similar catch for every endpoint.

As mentioned before, in large appplications with lot of controllers and CRUD-operations you would put a lot of time in repetitive code. For this problem Spring invented a more cryptic way of exceptionhandling. There is a lot to find on this topic, but I use my own approach based on some of these tutorials. Of course there can be optimizations, so feel free to make my effort even more practical.
ExceptionHandler
The first thing I do, is write a class that handles exceptions. Apart from a constructor that initializes a dictionary, this class has methods that will handle exceptions. In this case it will handle the MessageBusinessException, but other exceptions can be integrated as well. The important part to notice is the annotation @ControllerAdvice. The @ControlleAdvice allows you to apply global code to a large number of controllers. It will handle exceptions across the whole application in one global handling component and returns an error response based on the given businessexception. In this case only a MessageBusinessException, but for more complex applications other exceptions can be written. In the controller itself you don’t have to do anything, except for maybe erasing all try/catch-constructions I want to replace.

For the creation of an errormessage I’ve written a helper-method. The createErrorResponse is a helpermethod which returns the HttpStatus and response.
Keeping exceptions in key-value pairs
Status and response are filtered out of the ApiErrorDictionary which is basically an unmodifiable map. It uses a model ApiError which in my example only contains a string to respond with a message. The benefit of this dictionary is standardizing responses which can be the same in a lot of controllers. Instead of using an unmodifiable map, I could put the the key-value pairs in a database. Again, the idea is to mow all repetitive lines of code, which in complicated apps can add up.

MessageBusinessException
There is one last class that needs to be added. An exception is often thrown in the service and then it goes up, through the controller, to the user. In order to throw custom exceptions, you’ll need to write a custom class which extends RuntimeException. I tend to write such a businessexceptionclass per feature I implement. In my democode it’s part of the servicepackage, but it could be another choice to structure your code as package by feature and declare a businessexceptionclass per feature. Anyhow, the MessageBusinessException would look like this.

Flow
With this construction in place I can easily integrate new exceptions. I only need to update the BusinessexceptionClass in the feature, add a key/value-pair to the ApiErrorDictionary and update the according service by throwing the exception. Easy peasy, oh so breezy :-).
Git
This tutorial might be a bit short, but you can find the code on the github-account.
< Keep reading />
More from our team
Explore more insights, tips, and deep dives from the CraftCode team.
Let's get to work!
Ready to turn your vision into reality?
Let's build the systems that power your growth.