
# @ComponentScan

`@ComponentScan()` makes the generator write a module with every bean declared in the directory of the annotated file and its subdirectories.

## Why

Dart in Flutter and AOT executables cannot search the program for annotated classes at run time. So the search happens at build time: `build_runner` runs `spring_core_dart_generator`, which reads the files under the directory and writes a `BeanModule`. You pass that module to the context.

Put `@ComponentScan()` on a top-level variable. Initialize it with the generated module, which has the same name prefixed with `$`. The generated file sits under `lib/spring_gen/`, mirroring the path of your file.

A Spring Boot application uses `@SpringBootApplication()` instead, which scans too.

Nested scans: a subdirectory with its own `@ComponentScan` belongs to that scan, and its filters apply there. The enclosing scan imports its module instead of scanning its files again, so every bean is registered once. One directory has one scan root.

### Filters

Filters change what counts as a bean. They are evaluated at build time.

- `includeFilters`: classes matching one of them are beans even without `@Component`. Abstract classes never are.
- `excludeFilters`: classes matching one of them are never beans, whatever else says. Their `@Bean` methods are skipped with them.
- `useDefaultFilters: false`: `@Component` (and its subclasses) no longer marks a bean; only the include filters do.
- `lazyInit: true`: every scanned bean is created on first use, unless annotated `@Lazy(false)`.

A `Filter` matches in one of three ways (`FilterType`):

| `type` | Uses | Matches a class that |
|---|---|---|
| `FilterType.annotation` (default) | `classes` | carries one of the annotations (or a subclass of one). |
| `FilterType.assignableType` | `classes` | is, extends, implements or mixes in one of the types. |
| `FilterType.regex` | `pattern` | has a name matching one of the regular expressions. |

## Example

```dart
// lib/src/beans.dart: everything under lib/src/ is scanned.
import 'package:spring_core_dart/spring_core_dart.dart';
import 'package:my_app/spring_gen/src/beans.spring.dart';

@ComponentScan()
final BeanModule appBeans = $appBeans;
```

```dart
// lib/src/plugins/beans.dart: a scan with filters.
import 'package:spring_core_dart/spring_core_dart.dart';
import 'package:my_app/spring_gen/src/plugins/beans.spring.dart';

// Every Plugin class is a bean without @Component, classes whose name
// ends in Stub never are, and every bean is created on first use.
@ComponentScan(
  includeFilters: [
    Filter(type: FilterType.assignableType, classes: [Plugin]),
  ],
  excludeFilters: [
    Filter(type: FilterType.regex, pattern: [r'.*Stub$']),
  ],
  lazyInit: true,
)
final BeanModule pluginBeans = $pluginBeans;

abstract interface class Plugin {
  String get id;
}
```

```dart
// bin/main.dart
void main() {
  // appBeans already imports pluginBeans (nested scan); passing both
  // is harmless.
  final context = ApplicationContext.run([appBeans]);
  context.close();
}
```

Run the generator after changing annotations:

```bash
dart run build_runner build
```

## Parameters

`ComponentScan`:

| Name | Type | Default | Meaning |
|---|---|---|---|
| `includeFilters` | `List<Filter>` | `[]` | Classes matching one of these are beans even without `@Component`. |
| `excludeFilters` | `List<Filter>` | `[]` | Classes matching one of these are never beans. |
| `useDefaultFilters` | `bool` | `true` | Whether `@Component` and its subclasses mark a bean. |
| `lazyInit` | `bool` | `false` | Whether the scanned beans are created on first use. |

`Filter`:

| Name | Type | Default | Meaning |
|---|---|---|---|
| `type` | `FilterType` | `FilterType.annotation` | How the filter matches. |
| `classes` | `List<Type>` | `[]` | Annotation classes, or types the class must be assignable to. |
| `pattern` | `List<String>` | `[]` | Regular expressions matched against the class name. |

## In Spring (Java)

```java
@Configuration
@ComponentScan(
    basePackages = "com.example.plugins",
    includeFilters = @ComponentScan.Filter(
        type = FilterType.ASSIGNABLE_TYPE, classes = Plugin.class),
    excludeFilters = @ComponentScan.Filter(
        type = FilterType.REGEX, pattern = ".*Stub"),
    lazyInit = true)
class PluginConfig {}

var context = new AnnotationConfigApplicationContext(PluginConfig.class);
```

Differences that matter:

- Spring scans the classpath at startup, by package. Here the generator scans a directory at build time, and the result is a variable you pass to the context.
- There are no `basePackages`: the directory of the annotated file is the base.
- `FilterType.ASPECTJ` and `FilterType.CUSTOM` do not exist.

## See also

- [BeanModule](bean-module.md)
- [@Component](component.md)
- [@Import](import.md)
- [@Lazy](lazy.md)
- [ApplicationContext](application-context.md)
