Aspect Oriented Programming for?Babies
Aspect Oriented Programming is a powerful tool that enables a clean modularization of “crosscutting” concerns of enterprise applications; such as error handling, transaction management, monitoring and logging, just to name a few.
As part of this tutorial, we’ll explore Spring AOP and see how it works.
Aspect-Oriented Programming Illustrated
AOP terminology can be confusing, so the following slides illustrate the terminology in a graphic way to make it easier to understand.
How Spring AOP?Works?
With a general understanding of Aspect Oriented Programming in hand, let’s put the theory into practice.
Maven Dependencies
First, let’s add the Spring Boot Starter AOP dependency to the pom.xml file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Defining the Join?Point
The Join Point in this example will be determined by a custom Java annotation.
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target(METHOD)
@Retention(RUNTIME)
public @interface EventMonitor {
}
We have specified the ElementType.METHOD property for the @Target annotation, which means the annotation only works on methods. As for @Retention, it indicates that the annotation is available at runtime according to RetentionPolicy.RUNTIME.
Below is an example that adds @EventMonitor as the join point for the execution of process().
@Component
public class MyEventProcessor {
@EventMonitor
public void process(MyEvent event) throws InterruptedException {
// doing something that takes time...
Thread.sleep(1000);
}
}
Creating the?Aspect
With our annotation in place, let’s start by declaring the Aspect. In order to accomplish this, we create a class annotated with @Aspect and @Component.
领英推荐
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class EventMonitorAspect {
}
This class does not have any logic yet, but here we will implement our Pointcuts and Advices.
Declaring Pointcuts
The key concept behind AOP lies in Join Points matched by Pointcuts.
Spring AOP only supports join points for method execution, so a pointcut matches the execution of methods on Spring beans.
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class EventMonitorAspect {
@Pointcut("@annotation(EventMonitor)")
private void annotationEventMonitor() {
}
}
The pointcut declaration has two parts:
Spring AOP supports the following Pointcut Designator (PCD) for use in pointcut expressions:
Learn more about pointcuts here.
Declaring Advice
Advice is associated with a pointcut expression and can take place before, after, or around method executions matched by the pointcut.
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.time.Instant;
@Aspect
@Component
public class EventMonitorAspect {
@Pointcut("@annotation(EventMonitor)")
private void annotationEventMonitor() {
}
@Before("annotationEventMonitor()")
public void monitorBefore() {
long start = System.currentTimeMillis();
System.out.println("Processing started at " + Instant.ofEpochMilli(start) + "ms");
}
@Around("annotationEventMonitor()")
public Object monitorAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object arg0 = joinPoint.getArgs()[0];
System.out.println("Processing event: " + arg0);
return joinPoint.proceed();
}
@After("annotationEventMonitor()")
public void monitorAfter() {
long end = System.currentTimeMillis();
System.out.println("Processing ended at " + Instant.ofEpochMilli(end) + "ms");
}
}
Here:
Learn more about advice here.
Testing the Implementation
Now, let’s create an integration test to verify our implementation.
@SpringBootTest
class MyEventProcessorTest {
@Autowired
MyEventProcessor processor;
@Test
void process_test() throws InterruptedException {
processor.process(
new MyEvent(UUID.randomUUID(), "Example")
);
assert true;
}
}
In the code snippet above, SpringBootTest is used to make a simple integration test.
Let's run mvn verify in the terminal, and check the results.
The log shows that once the method annotated with @EventMonitor is executed, additional logic in the aspect is added to the object.
Thanks for reading. I hope this was helpful!
The example code is available on GitHub.