Introduction
Annotations in Java are widely used to provide metadata and declarative functionality. In Spring, annotations like @Autowire, @Transactional, and @RestController are frequently used to simplify configuration. However, Spring also allows developers to create custom annotations to handle specific behaviors or metadata needs in their applications. In this blog, we will explore how to create and process custom annotations in Spring, with real-world examples and use cases.
What Are Annotations in Spring ?
Annotations in Spring are a form of metadata that can be applied to classes, methods, or fields to convey additional information. They enable declarative programming, allowing you to perform various tasks without writing explicit code to handle configuration or logic.
Common Spring annotations:
- @Autowired for dependency injection
- @Transactional for managing transactions.
- @RequestMapping for handling HTTP requests.
Creating Custom Annotations in Spring
Step 1: Define a Custom Annotation
To create a custom annotation, use the @Interface keyword. You can define the scope, retention policy, and target of the annotation.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // Annotation available at runtime
@Target(ElementType.METHOD) // Can only be applied to methods
public @interface CustomAnnotation {
String value() default "default message";
}
- Retention(RetentionPolicy.RUNTIME): Ensures the annotation is available during runtime.
- @Target(ElementType.METHOD): Specifies that the annotation can only be applied to methods.
Applying the Custom Annotation
Once the annotation is created, you can apply it to methods like this:
public class MyService {
@CustomAnnotation(value = "Executing service method")
public void executeService() {
System.out.println("Service logic executed");
}
}
The annotation can now be used to mark methods where specific behavior or metadata needs to be applied.
Processing Custom Annotations with Spring AOP
To handle custom annotations in Spring, we typically use Spring AOP. The following example demonstrates how to intercept methods with the @CustomAnnotation using aspect.
Step 1: Create an Aspect
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CustomAnnotationAspect {
@Before("@annotation(customAnnotation)")
public void handleCustomAnnotation(CustomAnnotation customAnnotation) {
System.out.println("Intercepted method: " + customAnnotation.value());
}
}
@Before(“@annotation(customAnnotation)”): This advice is triggered before any method annotated with @CustomAnnotation is called. The customAnnotation parameter gives access to the annotation’s properties.
Use Cases for Custom Annotation in Spring
Custom annotations can be incredibly useful for encapsulating cross-cutting concerns. Here are some real-world scenarios:
Logging with Custom Annotations
Create an annotation like @LogExecutionTime to log the execution time of a method
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {
}
Then, use AOP to measure execution time:
@Aspect
@Component
public class LoggingAspect {
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long timeTaken = System.currentTimeMillis() - startTime;
System.out.println(joinPoint.getSignature() + " executed in " + timeTaken + "ms");
return proceed;
}
}
Security with Custom Annotations
You can create a custom annotation to enforce security roles.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Secured {
String role() default "USER";
}
And handle it with an aspect:
@Aspect
@Component
public class SecurityAspect {
@Before("@annotation(secured)")
public void checkSecurity(Secured secured) {
String requiredRole = secured.role();
// Perform role validation logic
System.out.println("Checking if user has role: " + requiredRole);
}
}
Transactional Behavior
Although Spring provides @Transactional for transaction management, you can create a custom annotation to tailor transactional behavior:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomTransactional {
String value() default "defaultTransaction";
}
Then manage transactions declaratively:
@Aspect
@Component
public class TransactionAspect {
@Before("@annotation(customTransactional)")
public void startTransaction(CustomTransactional customTransactional) {
System.out.println("Starting transaction: " + customTransactional.value());
// Start transaction logic
}
}
Conclusion
Custom annotations in Spring allow developers to simplify cross-cutting concerns and encapsulate repetitive behaviors in a clean, declarative way. By combining custom annotations with Spring AOP, you can enhance code readability modularity, and maintainability.
Custom annotations provide flexibility to define rules for logging, security, validation, or other behaviors, all while keeping your code logic free of boilerplate code.