skip to content

What is an ImportSelector, and how does selectImports work?

level: middleimportance: must knowfreq 45%

answer

  1. String[] selectImports(AnnotationMetadata)
  2. returned names imported as if in @Import
  3. read @Enable attributes from metadata
  4. runs at parse time — no bean injection, but Aware works
  5. AdviceModeImportSelector base class

basics

~20 s

ImportSelector is a Spring interface with one method, selectImports(AnnotationMetadata), that returns an array of fully-qualified class names. Spring imports each returned class as if you had listed it in @Import, letting you choose configs dynamically.

solid answer

~40 s

ImportSelector is an interface you reference from @Import. Its single method, String[] selectImports(AnnotationMetadata importingClassMetadata), returns the fully-qualified names of classes to register — usually @Configuration classes — and Spring imports each one as though it were written directly in @Import. The AnnotationMetadata parameter is the metadata of the class that carried the @Import (or the @Enable annotation), so you can read attributes from that annotation and branch on them. This powers configuration that varies by annotation attributes, classpath contents, or environment. A selector can also implement Aware interfaces (EnvironmentAware, BeanFactoryAware, ResourceLoaderAware, BeanClassLoaderAware) to make decisions, since those are injected before selectImports runs. It cannot inject fully-initialized beans — it executes during configuration parsing, before beans exist.

code

java · 23 lines
java
public class StorageSelector implements ImportSelector, EnvironmentAware {
    private Environment env;

    @Override public void setEnvironment(Environment environment) {
        this.env = environment; // injected BEFORE selectImports runs
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // read an attribute of the @EnableStorage annotation on the importing class
        var attrs = importingClassMetadata
                .getAnnotationAttributes(EnableStorage.class.getName());
        String backend = attrs != null ? (String) attrs.get("value") : "local";
        if ("s3".equals(backend) || env.acceptsProfiles(Profiles.of("cloud"))) {
            return new String[]{ S3StorageConfig.class.getName() };
        }
        return new String[]{ LocalStorageConfig.class.getName() };
    }
}

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME)
@Import(StorageSelector.class)
public @interface EnableStorage { String value() default "local"; }

go deeper

for a junior

Know it returns class names that Spring imports like @Import entries.

for a middle

Explain the AnnotationMetadata parameter, reading @Enable attributes, and that it runs at parse time.

for a senior

Discuss Aware injection timing, why beans can't be injected, and the AdviceModeImportSelector pattern.

for a principal

Reason about it as the config-selection seam for library @EnableXxx annotations and its cost on the startup critical path.

**`ImportSelector`** (`org.springframework.context.annotation.ImportSelector`) is a functional-style extension point that lets you compute — at container-startup/config-parsing time — *which* configuration classes should be imported, instead of hard-coding them in `@Import`. **The one method:** ```java String[] selectImports(AnnotationMetadata importingClassMetadata); ``` It returns fully-qualified class names. Each returned name is treated exactly as if it had appeared in an `@Import` — so returned classes are usually `@Configuration` classes (they may themselves be, or reference, other selectors/registrars). **The `AnnotationMetadata` argument:** this is metadata about the class that triggered the import — i.e. the class annotated with `@Import(MySelector.class)`, or more usefully the class annotated with a custom `@EnableXxx` that is meta-annotated with `@Import(MySelector.class)`. From it you can read: - which annotations are present (`isAnnotated`, `hasAnnotation`), - **the attributes** of those annotations via `getAnnotationAttributes(...)`. That's how, for example, `@EnableCaching(mode = ...)` or `@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)` selects a different configuration class — the selector reads the `mode` attribute and returns proxy-based vs aspectj-based config accordingly. Spring provides `AdviceModeImportSelector` as a base class for exactly this pattern. **How it fits the lifecycle:** selectors run inside `ConfigurationClassPostProcessor` while it parses configuration classes — *before* any singleton beans are instantiated. Consequences: - You **cannot** `@Autowired` a real bean into a selector; beans don't exist yet. - You **can** implement callback `*Aware` interfaces and Spring will inject them *before* calling `selectImports`: `EnvironmentAware` (read properties/profiles), `BeanFactoryAware`, `BeanClassLoaderAware`, `ResourceLoaderAware`. This is the sanctioned way to make selection decisions (e.g. check a property, or `ClassUtils.isPresent(...)` for classpath detection). **Instantiation detail:** the selector class itself is *not* registered as a bean; Spring instantiates it directly (via its no-arg constructor) purely to drive parsing. **Ordering / regular vs deferred:** a plain `ImportSelector` is processed inline as the configuration class is parsed. Its cousin `DeferredImportSelector` is processed *after all* configuration classes are parsed — that ordering is what lets Spring Boot auto-configuration yield to user configuration (covered in a separate question). **Gotchas:** - Returning a class name that doesn't exist / can't be loaded throws at startup. - Returning duplicates is harmless (imports are de-duplicated). - Combine with `@Conditional` on the *returned* config classes for finer gating — but note `@Conditional` and selector logic are complementary, not the same mechanism. - Don't do heavyweight work here; it runs on the startup critical path. **When to use:** building a reusable `@EnableXxx` library annotation whose imported configuration depends on annotation attributes, the environment, or what's on the classpath — without forcing users to list config classes manually.

  • Can an ImportSelector autowire another bean to decide what to import? Why or why not?
    No. Selectors run during configuration-class parsing, before any beans are instantiated, so there's nothing to inject. Instead implement EnvironmentAware/BeanFactoryAware/etc.; Spring wires those callbacks before calling selectImports, and you can inspect the Environment or classpath to decide.
  • How does the selector know which attributes the user put on the @EnableXxx annotation?
    Through the AnnotationMetadata argument — call getAnnotationAttributes(EnableXxx.class.getName()) to read the annotation's attribute map from the importing class, then branch on those values (this is how @EnableTransactionManagement's mode is read).

saying these in an interview costs you the question

  • Saying selectImports returns bean instances (it returns class-name Strings)
  • Claiming you can @Autowired real beans into an ImportSelector
  • Confusing the AnnotationMetadata (the importing class) with the selector's own class
  • Thinking the selector class is itself registered as a bean

context