Implement Custom Annotations in Spring Boot for Logging, Security & Caching
This guide explains common scenarios for using custom Java annotations—such as logging, permission control, data validation, and caching—and walks through the step-by-step process of defining an annotation, applying it in Spring services, handling it with an AOP aspect, and testing the implementation in a Spring Boot application.
Common Scenarios for Custom Annotations
Logging : Record method entry, exit, parameters, return values and execution time before and after method execution.
Permission Control : Annotate controller methods to specify required permissions, enabling the system to enforce access checks based on the annotation.
Data Validation : Apply annotations on entity fields to validate length, format, etc., e.g., during user registration.
Cache Handling : Mark methods whose return values can be cached, specifying cache name and expiration.
Implementation Steps for Custom Annotations
1. Define the annotation : Use the @interface keyword. An annotation can contain members with default values.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// Define a custom annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {
// Member variable for a log prefix
String prefix() default "";
} @Retention(RetentionPolicy.RUNTIME)keeps the annotation at runtime for reflection. @Target(ElementType.METHOD) limits the annotation to methods. String prefix() default "" defines a member with a default empty string.
2. Use the annotation : Apply it to target elements, such as a service method.
@Service
public class UserService {
@LogExecutionTime(prefix = "UserService business method execution time: ")
public String getUserInfo() {
try {
Thread.sleep(1000); // Simulate business processing time
} catch (InterruptedException e) {
e.printStackTrace();
}
return new User("xuanwu");
}
}The getUserInfo method is annotated with @LogExecutionTime and a custom prefix.
3. Process the annotation : Create an AOP aspect that runs before and after the annotated method.
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogExecutionTimeAspect {
private static final Logger logger = LoggerFactory.getLogger(LogExecutionTimeAspect.class);
@Around("@annotation(logExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, LogExecutionTime logExecutionTime) throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed(); // Execute target method
long endTime = System.currentTimeMillis();
String methodName = joinPoint.getSignature().getName();
String prefix = logExecutionTime.prefix();
logger.info("{} {} method execution time: {} ms", prefix, methodName, (endTime - startTime));
return proceed;
}
} @Aspectmarks the class as an aspect. @Component registers it in the Spring container. @Around("@annotation(logExecutionTime)") defines an around advice that matches methods annotated with @LogExecutionTime. joinPoint.proceed() executes the target method; the aspect logs the execution time using the annotation’s prefix and method name.
4. Test the implementation with a controller.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/info")
public String getUserInfo() {
return userService.getUserInfo();
}
}Run the Spring Boot application and request /user/info. The log will show something like:
UserService business method execution time: getUserInfo method execution time: 1000 msSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Xuanwu Backend Tech Stack
Primarily covers fundamental Java concepts, mainstream frameworks, deep dives into underlying principles, and JVM internals.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
