
# BeanModule

A `BeanModule` holds the bean definitions the generator found in one scanned directory, with the property files, imports and embedded resources that come with them.

## Why

Dart in Flutter and AOT executables cannot search a program for annotated classes at run time. So [@ComponentScan](component-scan.md) makes the generator write a module at build time, and the application passes its modules to the context:

```dart
final context = ApplicationContext.run([appBeans]);
```

You normally never construct a module. Annotate a top-level variable with `@ComponentScan()` and initialize it with the generated module, which has the same name prefixed with `$`:

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

A library ships its beans as a module of its own. The application lists it next to its own module, or imports it.

Write a module by hand for a few beans in a test or a plug-in.

### What a module contains

- `definitions`: the bean definitions. Each is registered only if its conditions match.
- `propertySources`: the `@PropertySource` declarations of its classes. They are loaded before the definitions are decided, so conditions see the properties.
- `imports`: modules loaded before this one: those of nested `@ComponentScan` directories and of `@Import`ed configuration classes.
- `registrars`: the `@Import`ed [BeanRegistrar](bean-registrar.md)s, `ImportSelector`s and `ImportBeanDefinitionRegistrar`s.
- `resources`: the files of the package's `lib/resources/`, embedded by the generator. They are served as `resources:` locations, also in AOT, Flutter and the web. They are registered as soon as the module is created, so configuration files read before the context starts can find them.
- generated helpers: the injectors of `@Configurable` classes, the descriptions of `@Introspected` classes, and type tests used by annotations and AOP.

When the context loads a module, it first loads its `imports`, then adds its `propertySources` to the environment, then registers each definition whose conditions match and runs its `registrars`. A module imported twice registers its beans once.

### Adding modules

- Pass several modules to `ApplicationContext.run([...])` or to the `AnnotationConfigApplicationContext` constructor.
- Call `context.register(module)` before `refresh()`, for modules chosen at run time.
- Return modules from an `ImportSelector`, to choose a group of beans from the configuration.

## Example

```dart
// lib/src/beans.dart
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
// test/orders_test.dart
import 'package:my_app/src/beans.dart';
import 'package:spring_core_dart/spring_core_dart.dart';
import 'package:test/test.dart';

abstract interface class Clock {
  DateTime now();
}

class FixedClock implements Clock {
  @override
  DateTime now() => DateTime.utc(2026, 1, 1);
}

// A hand-written module with a fixed clock and a test property file.
final testBeans = BeanModule(
  'testBeans',
  [
    BeanDefinition<FixedClock>(
      name: 'clock',
      types: const [FixedClock, Clock],
      primary: true,
      create: (_) => FixedClock(),
    ),
  ],
  propertySources: const [
    PropertySourceDeclaration(
      ['file:test/test.properties'],
      ignoreResourceNotFound: true,
    ),
  ],
);

void main() {
  test('uses the fixed clock', () {
    final context = ApplicationContext.run([appBeans, testBeans]);
    expect(context.getBean<Clock>().now().year, 2026);
    context.close();
  });
}
```

## Parameters

`BeanModule(name, definitions, {...})`:

| Name | Type | Default | Meaning |
|---|---|---|---|
| `name` (positional) | `String` | required | The module name, shown in logs and errors. By convention the name of the annotated variable. |
| `definitions` (positional) | `Iterable<BeanDefinition<Object>>` | required | The bean definitions. |
| `propertySources` | `List<PropertySourceDeclaration>` | `[]` | Property files to add to the environment. |
| `imports` | `List<BeanModule>` | `[]` | Modules loaded before this one. |
| `registrars` | `List<ImportDeclaration>` | `[]` | Registrars and selectors to run, each wrapped in an `ImportDeclaration`. |
| `resources` | `Map<String, EmbeddedResource>` | `{}` | Embedded files served as `resources:`. |

The other parameters (`configurers`, `configurablePostConstruct`, `configurablePreDestroy`, `introspections`, `typeTests`, `typeDepths`, `aopProxies`) are written by the generator.

`PropertySourceDeclaration(locations, {...})`:

| Name | Type | Default | Meaning |
|---|---|---|---|
| `locations` (positional) | `List<String>` | required | Resource locations. May contain `${...}` placeholders. |
| `name` | `String?` | `null` | The property source name. Derived from the location when `null`. |
| `ignoreResourceNotFound` | `bool` | `false` | Whether a missing file is skipped instead of failing the refresh. |
| `factory` | `PropertySourceFactory` | `DefaultPropertySourceFactory()` | Turns each file into a property source. |
| `encoding` | `String?` | `null` | The charset of the files. UTF-8 when `null`. |
| `conditions` | `List<Condition>` | `[]` | The files are loaded only when all match. |

`ImportDeclaration(create, {conditions, importer})`: `create` is a function that returns the registrar or selector, such as `MessagingRegistrar.new`.

## In Spring (Java)

Spring has no direct counterpart. A module is what classpath scanning plus `@Configuration` parsing would register:

```java
var context = new AnnotationConfigApplicationContext();
context.scan("com.example.app");          // like appBeans
context.registerBean("clock", Clock.class, FixedClock::new);
context.refresh();
```

Differences that matter:

- Spring scans packages at startup. Here the scan happens at build time and the result is a value you pass to the context.
- Files under `lib/resources/` are embedded in the module, where Spring reads `classpath:` resources from the class path.

## See also

- [@ComponentScan](component-scan.md)
- [@Import](import.md)
- [BeanRegistrar](bean-registrar.md)
- [ApplicationContext](application-context.md)
- [@PropertySource](../config/property-source.md)
- [Resources](../resources/index.md)
