Spring annotation processor for generating component metadata indexes to optimize application startup performance
npx @tessl/cli install tessl/maven-org-springframework--spring-context-indexer@6.2.0Spring Context Indexer is a Java annotation processor that generates component metadata indexes during compilation to optimize Spring application startup performance. It scans classes annotated with Spring stereotypes and creates an index file that Spring uses to avoid runtime classpath scanning.
Note: This component is deprecated as of Spring Framework 6.1 in favor of the AOT (Ahead of Time) engine.
pom.xml:<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>6.2.10</version>
<optional>true</optional>
</dependency>For Gradle:
dependencies {
compileOnly 'org.springframework:spring-context-indexer:6.2.10'
}import org.springframework.context.index.processor.CandidateComponentsIndexer;The annotation processor works automatically during compilation when the dependency is present. No explicit instantiation is needed:
// Classes with Spring stereotypes are automatically indexed
@Component
public class MyService {
// Implementation
}
@Repository
public class MyRepository {
// Implementation
}The processor generates META-INF/spring.components containing:
com.example.MyService=org.springframework.stereotype.Component
com.example.MyRepository=org.springframework.stereotype.RepositoryThe Spring Context Indexer follows the standard Java annotation processing architecture:
CandidateComponentsIndexer implements javax.annotation.processing.ProcessorMETA-INF/spring.components in properties formatThe main annotation processor that generates component metadata indexes.
@Deprecated(since = "6.1", forRemoval = true)
public class CandidateComponentsIndexer implements Processor {
/**
* Returns the set of supported processor options (empty set)
*/
public Set<String> getSupportedOptions();
/**
* Returns the set of annotation types supported by this processor
* @return Set containing "*" (all annotations)
*/
public Set<String> getSupportedAnnotationTypes();
/**
* Returns the latest supported source version
*/
public SourceVersion getSupportedSourceVersion();
/**
* Initializes the processor with the processing environment
* @param env The processing environment
*/
public synchronized void init(ProcessingEnvironment env);
/**
* Processes the annotations for the current round
* @param annotations The annotation types requested to be processed
* @param roundEnv Environment for information about the current round
* @return false (does not claim the annotations)
*/
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
/**
* Returns completions for annotation values (returns empty list)
* @param element The element being annotated
* @param annotation The partial annotation being applied
* @param member The annotation member to complete
* @param userText The user's partial input
* @return Empty list
*/
public Iterable<? extends Completion> getCompletions(
Element element, AnnotationMirror annotation,
ExecutableElement member, String userText);
}Data structures representing component metadata collected during processing.
/**
* Container for candidate component metadata items
*/
class CandidateComponentsMetadata {
/**
* Default constructor creating empty metadata container
*/
public CandidateComponentsMetadata();
/**
* Adds a metadata item to the collection
* @param item The metadata item to add
*/
public void add(ItemMetadata item);
/**
* Returns an unmodifiable list of all metadata items
* @return List of metadata items
*/
public List<ItemMetadata> getItems();
/**
* String representation of the metadata
*/
public String toString();
}
/**
* Represents a single component entry with type and stereotypes
*/
class ItemMetadata {
/**
* Creates metadata for a component type with its stereotypes
* @param type The fully qualified class name
* @param stereotypes Set of stereotype markers (annotations)
*/
public ItemMetadata(String type, Set<String> stereotypes);
/**
* Returns the component type (fully qualified class name)
* @return The component type
*/
public String getType();
/**
* Returns the set of stereotypes for this component
* @return Set of stereotype markers
*/
public Set<String> getStereotypes();
}Collects and manages component metadata during annotation processing rounds.
/**
* Collects metadata during annotation processing rounds
*/
class MetadataCollector {
/**
* Creates a metadata collector
* @param processingEnvironment The processing environment
* @param previousMetadata Previously collected metadata or null
*/
public MetadataCollector(ProcessingEnvironment processingEnvironment,
CandidateComponentsMetadata previousMetadata);
/**
* Marks elements as processed for the current round
* @param roundEnv The round environment containing elements to process
*/
public void processing(RoundEnvironment roundEnv);
/**
* Adds a metadata item to the collection
* @param metadata The metadata item to add
*/
public void add(ItemMetadata metadata);
/**
* Returns the collected metadata, merged with previous rounds
* @return Complete component metadata
*/
public CandidateComponentsMetadata getMetadata();
}Handles reading and writing component metadata to the filesystem.
/**
* Stores component metadata in META-INF/spring.components
*/
class MetadataStore {
/** The path where component metadata is stored */
static final String METADATA_PATH = "META-INF/spring.components";
/**
* Creates a metadata store for the given processing environment
* @param environment The annotation processing environment
*/
public MetadataStore(ProcessingEnvironment environment);
/**
* Reads existing metadata from the filesystem
* @return Previously stored metadata or null if not found
*/
public CandidateComponentsMetadata readMetadata();
/**
* Writes component metadata to META-INF/spring.components
* @param metadata The metadata to write
* @throws IOException If writing fails
*/
public void writeMetadata(CandidateComponentsMetadata metadata) throws IOException;
}Interfaces and implementations for detecting component stereotypes.
/**
* Interface for providing stereotypes that match an element
*/
interface StereotypesProvider {
/**
* Returns the stereotypes present on the given element
* @param element The element to examine
* @return Set of stereotype names or empty set if none found
*/
Set<String> getStereotypes(Element element);
}
/**
* Detects stereotypes marked with @Indexed annotation
*/
class IndexedStereotypesProvider implements StereotypesProvider {
/** The annotation that marks indexed stereotypes */
private static final String INDEXED_ANNOTATION = "org.springframework.stereotype.Indexed";
/**
* Creates provider with the given type helper
* @param typeHelper Utility for type operations
*/
public IndexedStereotypesProvider(TypeHelper typeHelper);
/**
* Returns stereotypes marked with @Indexed on the element or its annotations
* @param element The element to examine
* @return Set of indexed stereotypes
*/
public Set<String> getStereotypes(Element element);
}
/**
* Detects standard Jakarta/Java EE and legacy javax annotations
*/
class StandardStereotypesProvider implements StereotypesProvider {
/**
* Creates provider with the given type helper (package-private constructor)
* @param typeHelper Utility for type operations
*/
StandardStereotypesProvider(TypeHelper typeHelper);
/**
* Returns jakarta.* and javax.* annotations as stereotypes
* @param element The element to examine (must be class or interface)
* @return Set of standard stereotypes
*/
public Set<String> getStereotypes(Element element);
}
/**
* Provides package-info stereotype for package elements
*/
class PackageInfoStereotypesProvider implements StereotypesProvider {
/** The stereotype name for package elements */
public static final String STEREOTYPE = "package-info";
/**
* Returns package-info stereotype for package elements
* @param element The element to examine
* @return Set containing "package-info" for packages, empty otherwise
*/
public Set<String> getStereotypes(Element element);
}Utility class for working with Java type system during annotation processing.
/**
* Utilities for working with types during annotation processing
*/
class TypeHelper {
/**
* Creates type helper for the given processing environment
* @param env The processing environment
*/
public TypeHelper(ProcessingEnvironment env);
/**
* Gets the fully qualified type name from an element
* @param element The element to get type for
* @return Fully qualified type name or null
*/
public String getType(Element element);
/**
* Gets the fully qualified type name from an annotation mirror
* @param annotation The annotation to get type for
* @return Fully qualified annotation type name or null
*/
public String getType(AnnotationMirror annotation);
/**
* Gets the fully qualified type name from a type mirror
* @param type The type mirror to convert
* @return Fully qualified type name or toString() result
*/
public String getType(TypeMirror type);
/**
* Returns the superclass element of the given element
* @param element The element to get superclass for
* @return Superclass element or null if element represents Object
*/
public Element getSuperClass(Element element);
/**
* Returns interfaces directly implemented by the element
* @param element The element to get interfaces for
* @return List of directly implemented interfaces
*/
public List<Element> getDirectInterfaces(Element element);
/**
* Returns all annotation mirrors for an element, handling exceptions
* @param e The element to get annotations for
* @return List of annotation mirrors or empty list if error occurs
*/
public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e);
}Utilities for serializing metadata to and from properties format.
/**
* Marshals component metadata as properties files
*/
abstract class PropertiesMarshaller {
/**
* Writes component metadata to an output stream in properties format
* @param metadata The metadata to write
* @param out The output stream to write to
* @throws IOException If writing fails
*/
public static void write(CandidateComponentsMetadata metadata, OutputStream out)
throws IOException;
/**
* Reads component metadata from an input stream in properties format
* @param in The input stream to read from
* @return Parsed component metadata
* @throws IOException If reading fails
*/
public static CandidateComponentsMetadata read(InputStream in)
throws IOException;
}
/**
* Properties implementation that maintains sorted key order
*/
class SortedProperties extends Properties {
/** System line separator */
static final String EOL = System.lineSeparator();
/**
* Creates sorted properties with comment control
* @param omitComments true to omit comments when storing
*/
SortedProperties(boolean omitComments);
/**
* Creates sorted properties from existing properties
* @param properties Source properties to copy from
* @param omitComments true to omit comments when storing
*/
SortedProperties(Properties properties, boolean omitComments);
// Overridden methods maintain sorted order:
// store(), storeToXML(), keys(), keySet(), entrySet()
}The annotation processor handles errors gracefully:
Common exceptions:
IllegalStateException: Thrown when metadata cannot be written to filesystemIOException: Thrown during file I/O operations in MetadataStoreThe processor is configured automatically through the Java annotation processing framework:
META-INF/services/javax.annotation.processing.ProcessorgetSupportedOptions())The processor generates a single file at compile time:
File: META-INF/spring.components
Format: Java properties file
Content: Component type to stereotype mappings
Example output:
com.example.service.UserService=org.springframework.stereotype.Service
com.example.repository.UserRepository=org.springframework.stereotype.Repository
com.example.controller.UserController=org.springframework.stereotype.Controller
com.example.config.AppConfig=org.springframework.stereotype.ComponentThis index is consumed by Spring's ApplicationContext to optimize component scanning at runtime.