HelloKoding

Practical coding guides

Spring Boot AOP Custom Annotation Example

You will learn aspect programming by implementing a Spring AOP custom annotation to log the execution time of a method without modify its code. AOP stands for Aspect-Oriented Programming

Add Spring AOP into your project

Add spring-boot-starter-aop into your project as a dependency on pom.xml or build.gradle file. The library versions can be omitted as it is resolved by the parent pom provided by Spring Boot

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

You can find the full pom.xml file as below

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.3.RELEASE</version>
	</parent>

	<groupId>com.hellokoding.spring</groupId>
	<artifactId>spring-aop-example</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Create a custom annotation

In Java, you can use @interface to define an annotation type as the following

LogExecutionTime.java

package com.hellokoding.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}

Target indicates the contexts in which an annotation type is applicable. The contexts can be ElementType.METHOD and ElementType.TYPE

Retention indicates how long annotations with the annotated type are to be retained. The default value is RetentionPolicy.CLASS which is only retained by the compiler. RetentionPolicy.RUNTIME is retained by both the compiler at compile-time and VM at run-time

Create an aspect

Use AspectJ annotations such as @Aspect and @Around to declare aspect and advice

LoggingAspect.java

package com.hellokoding.spring;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(* commandLineRunner(..))")
    private void commandLineRunner() {}

    //@Around("@annotation(LogExecutionTime)")
    //@Around("execution(* commandLineRunner(..))")
    @Around("commandLineRunner()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        Object proceed = joinPoint.proceed();

        long executionTime = System.currentTimeMillis() - start;

        System.out.printf("%s executed in %sms %s",
            joinPoint.getSignature(), executionTime, System.lineSeparator());

        return proceed;
    }
}

The logExecutionTime is only applied to method annotated with @LogExecutionTime thanks to this advice @Around("@annotation(LogExecutionTime)")

Apply to a method

Add @LogExecutionTime to a method

Application.java

package com.hellokoding.spring;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    @LogExecutionTime
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {
            Thread.sleep(100);
        };
    }
}

Run and test

You can run the application by typing the following command on the terminal console at the project root directory

$ mvn clean spring-boot:run

You would see something like this text in the console

CommandLineRunner com.hellokoding.spring.Application.commandLineRunner(ApplicationContext)
    executed in 11ms

Conclusion

In this tutorial, we learned to create a custom annotation and apply it to a method by using Spring AOP and AspectJ annotations. You can find the full source code at GitHub https://github.com/hellokoding/hellokoding-courses/tree/master/spring-examples/spring-aop-example

Follow HelloKoding