A custom annotation in Java is created with the @interface keyword, optionally combined with @Target and @Retention to control where it can be applied and how long it's retained at runtime.
Key Points: • @interface MyAnnotation { ... } defines the annotation type; elements inside it look like abstract method declarations and can have default values. • @Target specifies where the annotation can be applied -- TYPE, METHOD, FIELD, PARAMETER, etc. • @Retention controls its lifecycle: SOURCE (discarded by compiler), CLASS (in bytecode but not visible at runtime), or RUNTIME (available via reflection). • For an annotation to be usable by Spring AOP or a custom ConstraintValidator, RUNTIME retention is required so it can be read reflectively. • Once defined, the annotation is applied like any built-in one, e.g. @MyAnnotation above a method or class.
Example: A custom @LogExecutionTime annotation, combined with an AOP aspect that intercepts any method carrying it, lets you measure and log a method's execution time just by adding one annotation, without touching the method's body.
Code Example:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
@Aspect
@Component
public class ExecutionTimeAspect {
@Around("@annotation(LogExecutionTime)")
public Object logTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
log.info("{} took {}ms", pjp.getSignature(), System.currentTimeMillis() - start);
return result;
}
}Interview Tip: A concise interview answer is:
"I define it with @interface, set @Target to say where it can be applied, and @Retention(RUNTIME) if I need to read it reflectively, for example from an AOP aspect. That's exactly the pattern behind something like a custom @LogExecutionTime annotation paired with an aspect that logs method timing."