skip to content

How do includeFilters and excludeFilters work, and what are the FilterType options?

level: middleimportance: must knowfreq 60%

answer

  1. 5 FilterType: ANNOTATION, ASSIGNABLE_TYPE, REGEX, ASPECTJ, CUSTOM
  2. useDefaultFilters=true → includes are additive
  3. "only these" ⇒ useDefaultFilters=false
  4. exclude beats include
  5. CUSTOM = TypeFilter over MetadataReader (no class load)

basics

~10 s

@ComponentScan can narrow what it registers using includeFilters (register extra matching classes) and excludeFilters (skip matching classes). Each filter has a FilterType: ANNOTATION, ASSIGNABLE_TYPE, ASPECTJ, REGEX, or CUSTOM.

solid answer

~40 s

includeFilters and excludeFilters refine which scanned classes become beans. Each is a @Filter with a FilterType: ANNOTATION (match classes carrying a given annotation), ASSIGNABLE_TYPE (match subtypes/implementations of a given class/interface), REGEX (match the fully-qualified class name), ASPECTJ (an AspectJ type pattern), and CUSTOM (your own TypeFilter). The key subtlety is useDefaultFilters, which defaults to true: Spring implicitly includes @Component/@Service/@Repository/@Controller. includeFilters are additive on top of those defaults, so to scan ONLY your custom-annotated classes you must set useDefaultFilters=false. excludeFilters always take precedence — a class matching both is excluded. A classic use: exclude a specific @Configuration during tests, or include a non-stereotype-annotated marker. Filters make scanning selective rather than all-or-nothing.

code

java · 18 lines
java
// Register ONLY classes annotated @DomainService, nothing else
@Configuration
@ComponentScan(
    basePackages = "com.app",
    useDefaultFilters = false, // drop implicit @Component/@Service defaults
    includeFilters = @ComponentScan.Filter(
        type = FilterType.ANNOTATION, classes = DomainService.class))
class DomainConfig { }

// Keep stereotypes but skip a config + anything named *LegacyDao
@Configuration
@ComponentScan(
    basePackages = "com.app",
    excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = LegacyConfig.class),
        @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com\\.app\\..*LegacyDao")
    })
class AppConfig { }

go deeper

for a junior

Should know filters exist to include/exclude classes and name at least ANNOTATION/ASSIGNABLE_TYPE.

for a middle

Should list all FilterTypes, explain include=additive with useDefaultFilters, and exclude precedence.

for a senior

Should explain useDefaultFilters=false for 'only these', custom TypeFilter over MetadataReader, and real test/partitioning use cases.

for a principal

Should discuss metadata-based (ASM) inspection avoiding class loading, and design filters for modular context partitioning and startup cost.

## Filters: narrowing or widening what scanning registers By default `@ComponentScan` registers every class carrying a stereotype annotation. **Filters** let you override that policy. ### The two filter attributes - `includeFilters` — classes matching these are treated as candidate components **even if they lack a stereotype annotation** (additive). - `excludeFilters` — classes matching these are **skipped**, even if they would otherwise be picked up. Each entry is a `@ComponentScan.Filter` (nested annotation), configured with a `type` (a `FilterType` enum) and either `classes` or `pattern`. ### FilterType options 1. **`FilterType.ANNOTATION`** — matches classes annotated with the given annotation type. `classes = SomeAnnotation.class`. Most common. 2. **`FilterType.ASSIGNABLE_TYPE`** — matches classes that are assignable to (subclass/implement) the given type. `classes = SomeInterface.class`. 3. **`FilterType.REGEX`** — `pattern = "com\\.app\\..*Repository"` matches against the **fully-qualified class name** using a `java.util.regex` pattern. 4. **`FilterType.ASPECTJ`** — `pattern = "com.app..*Service+"` uses an **AspectJ type pattern** (requires AspectJ on the classpath). 5. **`FilterType.CUSTOM`** — `classes = MyTypeFilter.class` where the class implements `org.springframework.core.type.filter.TypeFilter`. Full programmatic control via `MetadataReader`/`MetadataReaderFactory` — you inspect class/annotation metadata without loading the class. ### The critical gotcha: useDefaultFilters `@ComponentScan(useDefaultFilters = ...)` defaults to **true**. When true, Spring installs implicit include filters for `@Component` (and thus `@Service`, `@Repository`, `@Controller`, plus `@ManagedBean`/`@Named` if present). Your `includeFilters` are **added on top**. So: - To register your stereotypes **plus** some extra type → keep `useDefaultFilters = true` and add an `includeFilter`. - To register **only** classes matching your custom filter → set `useDefaultFilters = false`, otherwise the default stereotypes are still swept in. ### Precedence `excludeFilters` win over `includeFilters` (and over the defaults). If a class matches both an include and an exclude, it is excluded. ### Common real uses - **Exclude a configuration in tests:** `excludeFilters = @Filter(type = ASSIGNABLE_TYPE, classes = ProductionSchedulingConfig.class)`. Spring Boot's `@SpringBootApplication` even exposes this via its own filter attributes and adds `TypeExcludeFilter`/`AutoConfigurationExcludeFilter` by default. - **Component partitioning:** scan only web-layer classes into one context by matching a custom `@WebLayer` annotation with `useDefaultFilters=false`. - **REGEX/ASPECTJ** for legacy code following naming conventions rather than annotations. ### Metadata, not reflection Under the hood filters operate on `MetadataReader` (ASM-based bytecode reading) — classes are inspected **without being loaded/initialized**, which is why a custom `TypeFilter` receives `MetadataReader` and `MetadataReaderFactory`, not a `Class<?>`. ### Gotchas - Forgetting `useDefaultFilters=false` when you meant "only these" — you get unexpected extra beans. - REGEX matches the **FQN string**, so anchor/escape dots carefully. - ASPECTJ requires the AspectJ weaver dependency at scan time. - Exclude filters can't remove a bean defined by an explicit `@Bean` method — filters only affect scanning.

  • You added an includeFilter but still see the default @Service beans registered. Why?
    useDefaultFilters defaults to true, so include filters are additive on top of the implicit stereotype filters. Set useDefaultFilters=false to register only your matched classes.
  • A class matches both an include and an exclude filter — what happens?
    It is excluded. excludeFilters take precedence over includeFilters and the default filters.

saying these in an interview costs you the question

  • Saying includeFilters replace the default stereotype filters (they add to them unless useDefaultFilters=false)
  • Thinking includeFilters take precedence over excludeFilters
  • Believing REGEX matches simple class name rather than fully-qualified name
  • Claiming a custom filter receives a loaded Class object rather than MetadataReader

context