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 makes the generator write a module at build time, and the application passes its modules to the context:

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 $:

@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

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

Example

// 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;
// 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:

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

Differences that matter:

See also