skip to content

How does DeferredImportSelector differ from a plain ImportSelector, and why does auto-configuration use it?

level: seniorimportance: should knowfreq 38%

answer

  1. deferred = runs AFTER all @Configuration parsed
  2. makes @ConditionalOnMissingBean work — user wins
  3. AutoConfigurationImportSelector is deferred
  4. supports @Order + Group batching
  5. Group.process + selectImports merges/sorts/filters

basics

~20 s

A plain ImportSelector runs inline while its config class is parsed. A DeferredImportSelector runs after all @Configuration classes have been processed. That late timing lets Spring Boot auto-configuration apply last, so user-defined beans and @Conditional checks take precedence.

solid answer

~30 s

Both return class names to import, but timing differs. A regular ImportSelector is processed immediately as the enclosing configuration class is parsed. A DeferredImportSelector is queued and processed only after every regular configuration class has been parsed and registered. That ordering is essential for Spring Boot auto-configuration: it runs last, so user configuration and conditions like @ConditionalOnMissingBean can see the full picture and win over defaults. DeferredImportSelectors also honor ordering via @Order/Ordered and support a Group abstraction (DeferredImportSelector.Group) that batches and sorts imports across multiple selectors — Boot uses AutoConfigurationImportSelector with a group to sort, filter, and de-duplicate auto-configuration classes efficiently.

code

java · 18 lines
java
// Library default-config that must yield to user beans:
public class DefaultsSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        return new String[]{ DefaultMetricsConfig.class.getName() };
    }
    // optional: batch/sort across selectors
    @Override public Class<? extends Group> getImportGroup() { return null; }
}

@Configuration
class DefaultMetricsConfig {
    @Bean
    @ConditionalOnMissingBean               // only if user didn't define one
    MeterRegistry meterRegistry() { return new SimpleMeterRegistry(); }
}
// Because the selector is deferred, the user's own MeterRegistry @Bean
// is parsed first, so @ConditionalOnMissingBean correctly backs off.

go deeper

for a junior

Know deferred selectors run later than regular ones; likely beyond junior depth.

for a middle

State the timing difference and that auto-config is deferred so user beans win.

for a senior

Explain the @ConditionalOnMissingBean ordering guarantee, @Order support, and the Group batching hook.

for a principal

Detail AutoConfigurationImportSelector/AutoConfigurationGroup: filtering, exclusion, sorting, and startup-performance trade-offs of the grouping design.

**`DeferredImportSelector`** (`org.springframework.context.annotation.DeferredImportSelector`) extends `ImportSelector`. It adds *no new method for the basic contract* — it still implements `String[] selectImports(AnnotationMetadata)` — but it changes **when** the selector is invoked and adds a grouping hook. **Timing — the core difference:** - A **plain `ImportSelector`** is evaluated *inline*, at the moment `ConfigurationClassPostProcessor` reaches the `@Import` while parsing a given configuration class. Its returned classes are then parsed like any other. - A **`DeferredImportSelector`** is *collected and held back*. All regular configuration classes (your app's `@Configuration`, `@ComponentScan` results, plain-`ImportSelector` results) are parsed **first**. Only after that whole pass does Spring process the deferred selectors and import what they return. **Why the deferral matters (auto-configuration):** Spring Boot's `@EnableAutoConfiguration` is meta-annotated with `@Import(AutoConfigurationImportSelector.class)`, which is a `DeferredImportSelector`. Auto-configuration classes are full of conditions like `@ConditionalOnMissingBean`, `@ConditionalOnBean`, `@ConditionalOnClass`, `@ConditionalOnProperty`. For `@ConditionalOnMissingBean` to correctly mean "only if the *user* didn't define one," the user's own configuration must be processed **before** the auto-config. Deferring guarantees auto-config runs last, so user beans always take precedence and Boot fills only the gaps. **Ordering among deferred selectors:** deferred selectors respect `org.springframework.core.Ordered` / `@Order` and the `getImportGroup()` grouping. Order controls the sequence in which multiple deferred selectors (and their groups) contribute imports. **The `Group` abstraction:** `DeferredImportSelector.Group` lets you *batch* selection across selectors: ```java interface Group { void process(AnnotationMetadata metadata, DeferredImportSelector selector); Iterable<Entry> selectImports(); } ``` A selector returns a group type from `getImportGroup()`. Spring feeds each matching selector into one shared `Group` instance via `process(...)`, then calls the group's `selectImports()` once to get the final, merged, ordered list of `Entry` (each pairs the originating `AnnotationMetadata` with an imported class name). Boot's `AutoConfigurationGroup` uses this to load the candidate list, apply `AutoConfigurationImportFilter`s (fast condition pre-filtering), remove exclusions and duplicates, and sort — far more efficiently than importing one-by-one. **Lifecycle / injection:** like plain selectors, deferred selectors run before beans are instantiated, can implement the `*Aware` interfaces (`EnvironmentAware`, `BeanFactoryAware`, `ResourceLoaderAware`, `BeanClassLoaderAware`), and cannot `@Autowired` beans. **Gotchas:** - If you write a library `@EnableXxx` whose config should *defer to* the user's beans, use a `DeferredImportSelector`, not a plain one. - Grouping only applies to deferred selectors; plain selectors don't have `getImportGroup()`. - Deferred selectors still run at startup on the critical path — keep the logic cheap; Boot pushes heavy filtering into `AutoConfigurationImportFilter` for speed. **When to use:** default/auto configuration that must yield to user overrides, or any scenario where imported config must be decided *after* seeing all user configuration; use a `Group` when you have many candidates to sort/filter/de-duplicate as a batch.

  • Why would @ConditionalOnMissingBean behave incorrectly if auto-configuration used a plain ImportSelector instead of a deferred one?
    A plain selector could be processed before the user's configuration is parsed. Then @ConditionalOnMissingBean would evaluate against an incomplete bean picture and might register a default even though the user defines their own bean later — breaking the 'user wins' contract. Deferring ensures user config is seen first.
  • What problem does DeferredImportSelector.Group solve that returning class names directly does not?
    It lets Spring collect candidates from many deferred selectors into one place and then sort, filter (e.g. via AutoConfigurationImportFilter), exclude, and de-duplicate them as a batch before importing — more efficient and giving global ordering rather than per-selector, one-at-a-time imports.

saying these in an interview costs you the question

  • Saying deferred selectors run before regular @Configuration classes
  • Claiming DeferredImportSelector adds a different primary method than selectImports
  • Not connecting the deferral to @ConditionalOnMissingBean / user-config precedence
  • Thinking plain ImportSelectors support Group/getImportGroup

context