What is an ImportSelector, and how does selectImports work?
answer
- String[] selectImports(AnnotationMetadata)
- returned names imported as if in @Import
- read @Enable attributes from metadata
- runs at parse time — no bean injection, but Aware works
- AdviceModeImportSelector base class
basics
~20 sImportSelector 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 sImportSelector 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 linespublic 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
Know it returns class names that Spring imports like @Import entries.
Explain the AnnotationMetadata parameter, reading @Enable attributes, and that it runs at parse time.
Discuss Aware injection timing, why beans can't be injected, and the AdviceModeImportSelector pattern.
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