ConfigurableListableBeanFactory
ConfigurableListableBeanFactory is the full interface of the bean container: besides handing out beans, it lets code change bean definitions and the container's setup before any bean is created.
Why
Most applications never need it. Beans are declared with annotations, and the generator registers them. It is for code that adjusts the container itself:
- a BeanFactoryPostProcessor that makes some beans lazy or checks that a bean exists;
- code that registers an object that is not a bean, such as one built in
main, so beans can inject it; - code that registers a custom scope or adds a bean post-processor;
- a test or tool that uses a container without a context.
You get it in two places: as the argument of BeanFactoryPostProcessor.postProcessBeanFactory, and as context.beanFactory. The latter is available before refresh().
DefaultListableBeanFactory is its implementation. Every application context owns one. Create one yourself for a small container without a context: a command-line tool, a test, a library that wires a few objects. Without a context there is no environment, no event delivery, no detection of post-processor beans and no lifecycle management.
The interfaces it combines
| Interface | Role |
|---|---|
BeanFactory |
Lookups: getBean, findBean, getBeanByName, getBeanProvider, containsBean, isTypeMatch, getType. |
ListableBeanFactory |
Listing: getBeanNamesForType, getBeansOfType, getSortedBeans, getBeanNamesForAnnotation, findAnnotationOnBean. |
HierarchicalBeanFactory |
parentBeanFactory, containsLocalBean. |
SingletonBeanRegistry |
registerSingleton, getSingleton, containsSingleton, singletonNames. |
ConfigurableBeanFactory |
Setup: post-processors, scopes, aliases, conversion, placeholder resolvers, dependency records, destruction. |
AutowireCapableBeanFactory |
autowireBean, initializeBean, destroyBean for objects the container did not create. |
DefaultListableBeanFactory also implements BeanDefinitionRegistry (registerBeanDefinition, removeBeanDefinition, ...).
Example
In a post-processor:
import 'package:spring_core_dart/spring_core_dart.dart';
// The slow `reports` bean starts on first use; `auditLog` must exist.
@Component()
class AdjustBeans implements BeanFactoryPostProcessor {
@override
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.getBeanDefinition('reports').lazy = true;
if (!beanFactory.containsBeanDefinition('auditLog')) {
throw StateError('An auditLog bean is required');
}
}
}
Before the context starts:
abstract interface class Clock {
DateTime now();
}
class SystemClock implements Clock {
@override
DateTime now() => DateTime.now();
}
void main() {
final context = AnnotationConfigApplicationContext([appBeans]);
context.beanFactory
// An object built here, injectable as a Clock.
..registerSingleton(
'clock',
SystemClock(),
types: const [SystemClock, Clock],
)
// A library expects 'primaryDb'; the application named it 'dataSource'.
..registerAlias('dataSource', 'primaryDb')
..registerScope('screen', SimpleMapScope());
context.refresh();
}
A container without a context:
class Repo {}
class Service {
Service(this.repo);
final Repo repo;
}
void main() {
final factory = DefaultListableBeanFactory()
..registerBeanDefinition(
BeanDefinition<Repo>(name: 'repo', create: (_) => Repo()),
)
..registerBeanDefinition(
BeanDefinition<Service>(
name: 'service',
dependencies: const [Dependency(Repo)],
create: (r) => Service(r.dependency<Repo>()),
),
)
..validateBeanGraph()
..preInstantiateSingletons();
final service = factory.getBean<Service>();
factory.destroySingletons();
}
Members
Specific to ConfigurableListableBeanFactory:
| Member | Meaning |
|---|---|
getBeanDefinition(name) |
The definition, to read or change. Its mutable fields (scope, lazy, primary, fallback, autowireCandidate, defaultCandidate, order, description) apply to beans created afterwards. |
registerResolvableDependency(type, value) |
Makes value injectable where exactly type is asked for, without making it a bean. |
validateBeanGraph() |
Checks every dependency, required placeholder, @DependsOn name and cycle without creating a bean. |
preInstantiateSingletons() |
Creates every non-lazy singleton, then calls SmartInitializingSingleton.afterSingletonsInstantiated. |
preInstantiateSingletonsAsync() |
The same, awaiting init callbacks that return a Future. |
freezeConfiguration(), isConfigurationFrozen |
A hint that the definitions will not change, so lookups can be cached. |
From ConfigurableBeanFactory:
| Member | Meaning |
|---|---|
addBeanPostProcessor(processor), beanPostProcessorCount |
Adds a BeanPostProcessor for beans created from now on. |
registerScope(name, scope), registeredScopeNames, getRegisteredScope(name) |
Custom scopes for @Scope(name). |
registerAlias(name, alias) |
A second name for a bean. |
parentBeanFactory |
The factory asked when a lookup finds nothing here. Settable. |
conversionService |
Converts @Value results to the target type. Settable. |
addEmbeddedValueResolver(resolver), resolveEmbeddedValue(value) |
The chain that resolves ${...} placeholders. |
beanExpressionResolver |
Evaluates #{...} expressions. Settable. |
registerDependentBean(beanName, dependentBeanName), getDependentBeans(name), getDependenciesForBean(name) |
The recorded dependencies, which decide the destruction order. |
isCurrentlyInCreation(name), isFactoryBean(name) |
Questions about one bean. |
destroySingletons(), destroySingletonsAsync(), destructionErrors |
Destroys the created singletons. |
destroyScopedBean(name) |
Destroys the current instance of a bean in a custom scope. |
From SingletonBeanRegistry: registerSingleton(name, singleton, {types}) registers a ready-made object. It is found by its name and by the listed types (its runtime type by default). No injection, init or destroy callback runs for it.
On DefaultListableBeanFactory only: the constructor takes an optional parentBeanFactory, and allowBeanDefinitionOverriding (false by default) lets a definition replace another with the same name.
In Spring (Java)
@Component
class AdjustBeans implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) {
factory.getBeanDefinition("reports").setLazyInit(true);
}
}
ConfigurableListableBeanFactory factory = context.getBeanFactory();
factory.registerSingleton("clock", new SystemClock());
factory.registerScope("screen", new SimpleThreadScope());
Differences that matter:
- Definitions carry closures and their lookup types instead of class names. There is no reflection.
registerSingletonmust list every type the object should be found by. Spring finds the supertypes by reflection.validateBeanGraph()has no Spring counterpart. Spring finds missing beans and cycles only while creating beans.- Circular references are always an error. There are no early singleton references.
getBean<T>({qualifier})andgetBeanByName(name)replace the overloads ofgetBean.