Creating Custom Annotations in Spring Boot: A Step-by-Step Guide


Annotations are a powerful feature in Java, enabling you to add metadata to your code. In Spring Boot, annotations play a vital role—from defining components and wiring dependencies to configuring various behaviors. But what if the built-in annotations don’t meet your specific needs? That’s where custom annotations come into play. They allow you to encapsulate repetitive or cross-cutting logic, such as logging or validation, in a reusable and declarative way.
In this post, we’ll cover:
What Are Annotations?
Why They’re Useful in Spring Boot
Adding Required Dependencies in Gradle
Defining a Custom Annotation (with @Target and @Retention)
Creating an Aspect Class to Process the Annotation
Applying the Custom Annotation
Enabling AspectJ Auto-Proxy Support
Summary
1. What Are Annotations?
Annotations are a form of metadata in Java. They don’t directly change program semantics but can be used by tools and frameworks to generate code, configure behavior, or enforce validation rules. For example, Spring Boot relies on annotations like @Component
, @Service
, and @Autowired
to streamline configuration and dependency injection.
2. Why Are Annotations Useful in Spring Boot?
In Spring Boot, annotations help to:
Simplify Configuration: Replace verbose XML configurations.
Enhance Readability: Declaratively define bean behavior.
Handle Cross-Cutting Concerns: Easily implement logging, security, and transactions using Aspect-Oriented Programming (AOP).
Custom annotations take these benefits further by allowing you to:
Encapsulate frequently used logic.
Reduce boilerplate code.
Create a domain-specific language that makes your code self-explanatory.
3. Adding Required Dependencies in Gradle
To work with Spring AOP and AspectJ, ensure your build.gradle
includes the following dependencies:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.aspectj:aspectjrt:1.9.7'
implementation 'org.aspectj:aspectjweaver:1.9.7'
}
These dependencies allow you to use Spring Boot along with AspectJ for advanced AOP capabilities.
4. Defining a Custom Annotation
Let’s create a custom annotation called @LogExecutionTime
which will be used to log how long a method takes to execute. We define the annotation using the @interface
keyword along with metadata annotations.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // Specifies where this annotation can be applied
@Retention(RetentionPolicy.RUNTIME) // Specifies that this annotation will be available at runtime
public @interface LogExecutionTime {
}
Here,
@Target(ElementType.METHOD) limits usage to methods.
@Retention(RetentionPolicy.RUNTIME) ensures the annotation is available at runtime for reflection and AOP.
5. Creating an Aspect Class to Process the Annotation
Now, we’ll create an aspect that intercepts methods annotated with @LogExecutionTime
and logs their execution time.
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
In this aspect:
@Aspect
designates the class as an aspect.@Component
registers it as a Spring bean.The
logExecutionTime
method is annotated with@Around
, indicating it should wrap the execution of methods annotated with@LogExecutionTime
.ProceedingJoinPoint
allows control over the method execution, enabling actions before and after the method invocation.
6. Applying the Custom Annotation
With the annotation and aspect in place, you can now use your custom annotation on any method you want to monitor.
package com.example.controllers;
import com.example.annotations.LogExecutionTime;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SampleController {
@LogExecutionTime // Custom annotation to log method execution time
@GetMapping("/greet")
public String greet() throws InterruptedException {
// Simulate processing time
Thread.sleep(500);
return "Hello, world!";
}
}
Every time the /greet
endpoint is hit, the execution time of the greet()
method will be printed to the console.
7. Enabling AspectJ Auto-Proxy Support
To enable support for AspectJ auto-proxying in your Spring Boot application, add the @EnableAspectJAutoProxy
annotation to your main application class:
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@EnableAspectJAutoProxy // Enables support for handling components marked with @Aspect
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
This ensures that Spring scans for aspects and applies your custom logic during runtime.
8. Summary
Custom annotations in Spring Boot offer a clean and declarative way to inject cross-cutting behavior into your application. In this guide, we covered:
Annotations & Their Role: How they add metadata and simplify configuration.
Dependencies: Adding the Spring Boot AOP and AspectJ dependencies using Gradle.
Annotation Definition: Using
@Target
and@Retention
to define a custom annotation.Aspect Creation: Building an aspect that intercepts annotated methods and applies custom behavior (e.g., logging execution time).
Application: Applying the custom annotation on a method and enabling AspectJ auto-proxy support with
@EnableAspectJAutoProxy
.
Using these techniques, you can reduce boilerplate code, enhance maintainability, and enforce a consistent coding style across your Spring Boot applications.
Happy coding!
Subscribe to my newsletter
Read articles from Shivam Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Shivam Kumar
Shivam Kumar
As a recent Computer Science graduate with a passion for backend development, I’m skilled in JavaScript, Node.js, React, Express, MongoDB, and MySQL. I have experience with Docker, Kubernetes, RabbitMQ, and gRPC, and am familiar with RESTful APIs and microservices. Dedicated and analytical, I’m improving my communication skills and enjoy watching anime in my free time. I’m excited to join a team focused on innovation and growth.