How do includeFilters and excludeFilters work, and what are the FilterType options?
answer
- 5 FilterType: ANNOTATION, ASSIGNABLE_TYPE, REGEX, ASPECTJ, CUSTOM
- useDefaultFilters=true → includes are additive
- "only these" ⇒ useDefaultFilters=false
- exclude beats include
- 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 sincludeFilters 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// 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
Should know filters exist to include/exclude classes and name at least ANNOTATION/ASSIGNABLE_TYPE.
Should list all FilterTypes, explain include=additive with useDefaultFilters, and exclude precedence.
Should explain useDefaultFilters=false for 'only these', custom TypeFilter over MetadataReader, and real test/partitioning use cases.
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