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:

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:

See also