skip to content

What is @Indexed and the compile-time candidate component index in Spring, and why does it exist?

level: juniorimportance: should knowfreq 15%

answer

  1. @Indexed = marker meta-annotation, Spring 5
  2. spring-context-indexer -> META-INF/spring.components
  3. @Component already meta-annotated @Indexed
  4. build-time discovery vs runtime ASM scan
  5. faster startup, same beans

basics

~10 s

@Indexed marks a class or stereotype so a build-time tool records it in a file, META-INF/spring.components. At startup Spring reads that file instead of scanning the whole classpath, which makes startup faster.

solid answer

~40 s

@Indexed (org.springframework.stereotype.Indexed, since Spring 5) is a marker meta-annotation. When you add the spring-context-indexer annotation processor to your build, it scans sources at compile time and generates META-INF/spring.components, listing every indexed class and the stereotypes it carries. At runtime Spring builds a CandidateComponentsIndex from those files and consults it during component scanning instead of reading every class's bytecode with ASM. Because @Component is itself meta-annotated with @Indexed, all standard stereotypes (@Service, @Repository, @Controller, @Configuration) are indexed automatically. The payoff is faster startup for large codebases with many components; the cost is an extra build dependency and a file kept in sync via recompilation.

code

java · 9 lines
java
// You do NOT annotate normal beans with @Indexed directly.
// @Service is already indexed because @Component is meta-annotated with @Indexed:

@Service // -> ends up in META-INF/spring.components automatically
public class OrderService {
}

// Generated META-INF/spring.components line:
// com.example.OrderService=org.springframework.stereotype.Component

go deeper

for a junior

Know that @Indexed feeds a build-time file, META-INF/spring.components, read at startup to speed up component discovery.

for a middle

Explain that spring-context-indexer generates the file and that @Component is already meta-annotated with @Indexed, so standard stereotypes are covered.

for a senior

Frame it as a build-time alternative to runtime ASM scanning, note CandidateComponentsIndex(Loader), and that it doesn't change results, only speed.

for a principal

Position it against Spring Boot 3 AOT, discuss when it's worth the build complexity, and the completeness/consistency requirements.

## The problem it solves Normally, `@ComponentScan` triggers **runtime classpath scanning**: Spring walks the configured base packages, reads each `.class` file's metadata using ASM bytecode reading (not full classloading), and tests it against include/exclude filters to decide whether it is a bean candidate. For a large application with many classes, that file-walking plus metadata-reading has a real startup cost. ## What the index is The **compile-time candidate index** moves component discovery to build time. The `spring-context-indexer` is a Java **annotation processor** that runs during compilation. It inspects your annotated types and emits a properties-style file at `META-INF/spring.components`. Each line maps a fully-qualified class name to the comma-separated stereotype annotations it carries, e.g. `com.example.MyService=org.springframework.stereotype.Component`. ## The role of `@Indexed` `@Indexed` (package `org.springframework.stereotype`, added in Spring 5.0) is a **marker** that says "instances of the annotated stereotype should be recorded in the index." Crucially, `@Component` is itself meta-annotated with `@Indexed`. Because `@Service`, `@Repository`, `@Controller`, and `@Configuration` are all meta-annotated with `@Component`, every standard stereotype is indexed automatically — you do not annotate your own beans with `@Indexed` directly. You only add `@Indexed` when defining a *custom* stereotype annotation that is NOT built on `@Component`. ## Runtime side At startup, `CandidateComponentsIndexLoader.loadIndex(ClassLoader)` loads ALL `META-INF/spring.components` files on the classpath and returns a `CandidateComponentsIndex` (or `null` if none exist). `ClassPathScanningCandidateComponentProvider` then queries the index via `getCandidateTypes(basePackage, stereotype)` instead of walking the filesystem. ## When to use it Use it for large applications where startup time matters and you control the build. It is transparent: enabling it never changes which beans are found (assuming the index is complete) — only *how* they are found. Modern Spring Boot 3 AOT processing largely supersedes it, but the mechanism remains valid.

  • Do you have to add @Indexed to your @Service classes for them to be indexed?
    No. @Component is meta-annotated with @Indexed, and every standard stereotype is meta-annotated with @Component, so they are indexed automatically. You add @Indexed only when creating a custom stereotype not derived from @Component.
  • Does using the index change which beans get registered?
    It should not — the index is just a faster discovery path. Given a complete index, the same candidates are found. It only changes how discovery happens, not the result.

saying these in an interview costs you the question

  • Thinking you must annotate every bean with @Indexed manually
  • Believing @Indexed replaces @Component (it is a marker, not a stereotype)
  • Claiming the index changes the set of registered beans
  • Confusing it with runtime classpath scanning rather than a build-time alternative

context