@Configuration
@Configuration() marks a class whose @Bean methods create beans.
Why
Some objects cannot carry @Component():
- the class comes from another package (an HTTP client, a database driver);
- the constructor needs values that are not beans (a URL from the settings);
- creating the object takes a few lines of setup code.
A configuration class groups the methods that create such objects. Each @Bean method returns one bean, and its parameters are injected like constructor parameters. The configuration class is a bean too.
If every object of yours can carry @Component(), you do not need a configuration class.
Calls between @Bean methods
By default (proxyBeanMethods: true) the container creates a generated subclass of your configuration class. Its @Bean methods return the container's bean. So when one @Bean method calls another, it gets the existing singleton, not a second object. A prototype-scoped @Bean still gives a new instance per call. Static @Bean methods are not intercepted.
Because of the subclass, a class with instance @Bean methods must be extendable from another library: not final, sealed or interface, with a public generative constructor. Otherwise the build fails.
With proxyBeanMethods: false ("lite" mode) the methods run as written and there is no subclass. Then take other beans as method parameters instead of calling the other methods.
@Lazy, @Profile and conditions on the class apply to all its @Bean methods, unless a method says otherwise (@Lazy(false)).
Example
import 'package:spring_core_dart/spring_core_dart.dart';
// Classes from "another package": they cannot be annotated.
class Connection {
Connection(this.url);
final String url;
void connect() {}
void close() {}
}
class UserStore {
UserStore(this.connection);
final Connection connection;
}
abstract interface class Clock {
DateTime now();
}
class SystemClock implements Clock {
@override
DateTime now() => DateTime.now();
}
class Scheduler {
Scheduler(this.clock);
final Clock clock;
}
@Configuration()
class DataConfig {
// The URL comes from the configuration. connect() runs once the
// bean is created; close() is called at shutdown (inferred).
@Bean(initMethod: 'connect')
Connection connection(@Value(r'${db.url}') String url) => Connection(url);
// The Connection bean is injected as a parameter.
@Bean()
UserStore userStore(Connection connection) => UserStore(connection);
@Bean()
Clock clock() => SystemClock();
// Calling clock() returns the container's singleton, not a new clock.
@Bean()
Scheduler scheduler() => Scheduler(clock());
}
// Lite mode: no generated subclass. Dependencies come in as parameters.
@Configuration(proxyBeanMethods: false)
class LiteConfig {
@Bean()
Scheduler backupScheduler(Clock clock) => Scheduler(clock);
}
Parameters
| Name | Type | Default | Meaning |
|---|---|---|---|
name |
String? |
null |
The bean name of the configuration class. null gives the class name in lowerCamelCase. Named, unlike @Component's positional name. |
proxyBeanMethods |
bool |
true |
Whether a generated subclass makes @Bean methods return the container's beans. With false the methods are called as written. |
In Spring (Java)
@Configuration
class DataConfig {
@Bean(initMethod = "connect")
Connection connection(@Value("${db.url}") String url) {
return new Connection(url);
}
@Bean
UserStore userStore(Connection connection) {
return new UserStore(connection);
}
@Bean Clock clock() { return new SystemClock(); }
@Bean Scheduler scheduler() { return new Scheduler(clock()); }
}
@Configuration(proxyBeanMethods = false)
class LiteConfig { ... }
Differences that matter:
- Spring creates the subclass at run time with CGLIB. Here the generator writes it into the
.spring.dartfile, which is why the class must be extendable from another library. - Spring writes
@Configuration("name"). Here the name is the named argumentname:.