1 IntroductionThe main target of the aspect oriented programming is the separation of cross-cutting concerns. When we talk about cross-cutting concerns we are referring to generic functionality that is used in several places in our system or application. These concepts are, among others:
- Transaction management
- Error handling
The way to achieve this separation is by modularizing these concepts. This will allow us to keep our business logic classes clean, containing only the code for which the class was designed. If we don't modularize these concerns, it will lead to code tangling (the class contains different concerns) and code scattering (the same concern will be spread across the system).
In this example, we have a Spring MVC application that access the requested data (clients and orders) and shows a page with its information. We can take a look at the different layers:
In the above graphic, we can appreciate that there are functionalities spread across different classes (monitoring is implemented in every service), and some classes contain different concerns (for example, the class ClientController contains logging and exception handling). In order to fix that, we will write aspects to implement our cross-cutting concerns. The goal is to implement the following model:
Each class contains only the business logic related code, while the aspects will be responsible of intercepting the code in order to inject the cross-cutting concerns.
Let's see this with an example.
Source code can be found at github.
2 Checking controller codeClientController:
The objective of this controller consists in retrieving a client and returning a view showing its information but, as you can see, this code contains additional logic. In one hand, it handles exceptions that may be thrown by the service and it redirects to an error page. On the other hand, it generates logging information and notification sending in case of error. All this code is generic to all controllers in this application (and probably to other classes).
It's true that we could have used the @ControllerAdvice annotation to centralize exception handling, but the target of this post is to see how to accomplish it with Spring AOP.
The same happens with the order controller. I won't include it here because I don't want to make the post unnecessary long. If you want to check it out, you can get the source code included in the previous link.
3 Checking services codeClientService:
In addition to the service invocation, it also contains logging generation and monitoring of the execution time in each invocation.
We could also use aspects to modularize transaction management if we needed to use programmatic transaction management, but it's not the case in this example.
4 Data access layerClientRepositoryImpl:
This code does not contain any cross-cutting concern but I've included it to show all the sample application layers.
5 Activating AOPTo configure AOP, it is necessary to import the following dependencies:
In the Spring configuration file, we need to add the following tags:
The component-scan tag will search within the base package in order to find our aspects. To use auto scan, you not only need to define the aspect class with @Aspect annotation, but also you will need to include @Component annotation. If you don't include @Component you will need to define the aspect in the xml configuration file.
6 Centralizing error handlingWe'll write an aspect with an @Around advice. This advice will intercept every method annotated with @RequestMapping annotation and will be responsible of invoking it, catching exceptions thrown by the service.
The @Target annotation allows us to reference the intercepted class. Now we have the exception handling handled by the aspect so we can get rid of this logic in our controllers.
Just a note, you could have intercepted exceptions thrown by the controller with the following advice:
But be aware that this advice will not prevent the exception from propagating.