ApplicationContext

ApplicationContext is the running container: it holds the beans and gives access to the configuration, events, translated messages and resource files.

Why

Most code never touches the context. Beans receive what they need by injection: other beans, an ApplicationEventPublisher, a MessageSource, the Environment. You meet the context in these places:

There are three types:

Starting

ApplicationContext.run(modules) creates an AnnotationConfigApplicationContext and calls refresh(). ApplicationContext.runAsync(modules) calls refreshAsync() instead. Use it when an init callback returns a Future, or when a @PropertySource reads a file that is only available asynchronously, such as a Flutter asset.

refresh() does, in order:

  1. check the required properties;
  2. load the modules: their @PropertySource files and conditions decide which beans exist;
  3. run the BeanFactoryPostProcessors;
  4. register the BeanPostProcessors;
  5. set up the message source and the event multicaster, and register the listeners;
  6. check the bean graph: a missing or ambiguous dependency, or a cycle, fails here, before any constructor runs;
  7. create every singleton that is not lazy;
  8. start the auto-startup SmartLifecycle beans;
  9. publish ContextRefreshedEvent.

When a step fails, the lifecycle beans already started are stopped, the created singletons are destroyed and the error is rethrown. The context is then unusable. refresh() may be called once; a second call throws a StateError.

Construct AnnotationConfigApplicationContext yourself when something must happen between creating the context and starting it: register more modules or registrars, registerBean a bean in code, addBeanFactoryPostProcessor, set a parent.

Closing

close() publishes ContextClosedEvent, stops the running lifecycle beans and destroys the singletons, each after the beans that depend on it. It does not wait for asynchronous work. closeAsync() waits for SmartLifecycle stop callbacks and for destroy callbacks that return a Future. A second call does nothing.

registerShutdownHook() runs closeAsync() on SIGINT or SIGTERM, then ends the process with 128 + signal. It does nothing on the web.

Pausing

pause() stops the running lifecycle beans but keeps every bean, then publishes ContextPausedEvent. restart() starts the auto-startup beans again and publishes ContextRestartedEvent. A Flutter app calls them when it goes to the background and comes back. start() and stop() start and stop every lifecycle bean, auto-startup or not.

Replacing parts of the context

Declare a bean with one of these names to replace a part of the context:

Bean name Type Default
messageSource MessageSource an empty DelegatingMessageSource that asks the parent
applicationEventMulticaster ApplicationEventMulticaster a synchronous SimpleApplicationEventMulticaster
lifecycleProcessor LifecycleProcessor a DefaultLifecycleProcessor
conversionService ConversionService the environment's conversion service

Example

import 'package:spring_core_dart/spring_core_dart.dart';
import 'package:my_app/src/beans.dart';

const log = SpringLog('my_app');

class UserLoggedIn {
  UserLoggedIn(this.name);
  final String name;
}

void main() {
  // The `dev` profile is set before any bean definition is loaded.
  final context = ApplicationContext.run(
    [appBeans],
    environment: StandardEnvironment()..setActiveProfiles(['dev']),
  )..registerShutdownHook();

  // Beans.
  final auth = context.getBean<AuthFacade>();
  final audit = context.findBean<Audit>(); // null when absent
  final smtp = context.getBean<Transport>(qualifier: 'smtp');

  // Events, from code that is not a bean.
  final registration = context.addListener<UserLoggedIn>(
    (event) => log.info('logged in: ${event.name}'),
  );
  context.publishEvent(UserLoggedIn('ada'));
  registration.remove();

  // Messages, resources, configuration.
  final title = context.getMessage('app.title', locale: 'de');
  final template = context.getResource('resources:templates/mail.txt');
  final port = context.environment.getProperty('server.port');
}

A context set up before it starts:

Future<void> main() async {
  final context = AnnotationConfigApplicationContext([appBeans])
    ..register(pluginBeans)
    ..register(const MessagingRegistrar())
    ..registerBean<Clock>((_) => SystemClock(), name: 'clock')
    ..addBeanFactoryPostProcessor(MakeReportsLazy());
  await context.refreshAsync();
  try {
    context.getBean<App>().run();
  } finally {
    await context.closeAsync();
  }
}

Members

ApplicationContext (it is also a ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher and ResourcePatternResolver):

Member Meaning
static run(modules, {parent, environment, resourceLoader, id}) Creates a context for the modules and calls refresh().
static runAsync(modules, {...}) The same with refreshAsync().
getBean<T>({qualifier}) The bean of T. Throws when none or several match.
findBean<T>({qualifier}) The bean of T, or null when none matches.
getBeanByName(name) The bean with that name or alias. &name gives a FactoryBean itself.
getBeanProvider<T>({qualifier}) An ObjectProvider that looks the beans up when used.
getBeansOfType<T>(), getSortedBeans<T>() Every bean of T, by name or in order.
containsBean(name), getBeanNamesForType(type), getType(name) Questions about the beans, without creating them.
publishEvent(event) Sends an event to the listeners. Any object can be an event.
addListener<E>(listener, {order}) Calls a function for every event of E. Returns a ListenerRegistration.
getMessage(code, {args, defaultMessage, locale}) A translated message.
getResource(location), getResources(pattern) Files by location (resources:, file:, patterns).
environment Properties, profiles and conversion.
autowireCapableBeanFactory Injects objects the container did not create (@Configurable).
parent The parent context, or null.
id, displayName, startupDate For logs and diagnostics.

ConfigurableApplicationContext adds:

Member Meaning
register(component) Before refresh: adds a BeanModule, a BeanRegistrar, an ImportSelector or an ImportBeanDefinitionRegistrar.
addBeanFactoryPostProcessor(processor) A post-processor that runs before the post-processor beans.
addApplicationListener(listener), removeApplicationListener(listener) Listener objects that are not beans.
addProtocolResolver(resolver) A custom location prefix for getResource, such as asset:.
beanFactory The ConfigurableListableBeanFactory inside the context.
refresh(), refreshAsync() Starts the context. Call one of them once.
close(), closeAsync() Stops the lifecycle beans and destroys the singletons.
registerShutdownHook() Closes the context on SIGINT or SIGTERM.
start(), stop() Starts or stops every lifecycle bean.
pause(), pauseAsync(), restart() Pauses and resumes the pauseable lifecycle beans.
isActive, isClosed, isRunning The state of the context.

AnnotationConfigApplicationContext adds registerBean<T>(supplier, {...}), with the options of BeanRegistrar, and three template methods for subclasses: postProcessBeanFactory, onRefresh and onClose.

In Spring (Java)

var context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("dev");
context.register(AppConfig.class);
context.refresh();
context.registerShutdownHook();
AuthFacade auth = context.getBean(AuthFacade.class);
context.publishEvent(new UserLoggedIn("ada"));

Differences that matter:

See also