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

Shivam KumarShivam Kumar
4 min read

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:

  1. What Are Annotations?

  2. Why They’re Useful in Spring Boot

  3. Adding Required Dependencies in Gradle

  4. Defining a Custom Annotation (with @Target and @Retention)

  5. Creating an Aspect Class to Process the Annotation

  6. Applying the Custom Annotation

  7. Enabling AspectJ Auto-Proxy Support

  8. 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!

0
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.