Aspect Oriented Programming with Spring
Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature.)
One of the key components of Spring is the AOP framework. While the Spring IoC container does not depend on AOP, meaning you do not need to use AOP if you don’t want to, AOP complements Spring IoC to provide a very capable middleware solution.
Implementing a Custom Spring Aspect Oriented Programming Annotation
First, let’s add our Maven dependencies.
<dependencies> .... <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> .... </dependencies>
Creating a Custom Annotation
The annotation we are going to create is the one that will be used to log information about controller methods. Let’s create our annotation:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogControllerInfo { }
Creating the Aspect
Now that we have our annotation, let’s create our aspect. This is just the module that will encapsulate our cross-cutting concern, which is, in our case, the method execution time logging. All it is is a class,
@Aspect @Component public class ControllerAspect { }
Creating Our Pointcut and Advice
Now, let’s create our pointcut and advice. This will be an annotated method that lives in our aspect:
@Before("@annotation(LogControllerInfo)") public Object LogControllerInfo(ProceedingJoinPoint pjp) throws Throwable { return pjp.proceed(); }
Logging controller info
Now that we have our skeleton in place, all we need to do is add some extra logic to our advice. We will add a parameter that can be found in every controller. Every controller that has the HttpServletRequest parameter will be processed by the @LogControllerInfo annotation.
@Around("@annotation(LogControllerInfo) && args(..,request)") public Object LogControllerInfo(ProceedingJoinPoint jp, HttpServletRequest request) throws Throwable { System.out.println(jp.getSignature()); System.out.println(request.getMethod()); System.out.println(request.getServletPath()); return jp.proceed(); }
Controller method
According to the rules, the method has to have the LogControllerInfo annotation, and the HttpServletRequest parameter. For example:
@GetMapping("/{id}") @LogControllerInfo public ResponseEntity getOne(@PathVariable long id, HttpServletRequest request){ ... }
After execution, we should see the following logged to the console:
ResponseEntitycom.lilly021.web.rest.controller.AccountController.getOne(id,HttpServletRequest) GET /api/accounts/5