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:
- in
main, whereApplicationContext.run(or Spring Boot'sSpringApplication.run) creates and starts it; - in tests;
- in code outside the container that needs a bean, such as a Flutter widget;
- in a bean that must look beans up at run time or shut the application down. It can inject
ApplicationContextorConfigurableApplicationContext.
There are three types:
ApplicationContextreads: beans, events, messages, resources, the environment.ConfigurableApplicationContextadds what the owner of the context needs:register,refresh,close,pause,restart.AnnotationConfigApplicationContextis the only implementation. It builds its beans from BeanModules.
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:
- check the required properties;
- load the modules: their
@PropertySourcefiles and conditions decide which beans exist; - run the BeanFactoryPostProcessors;
- register the BeanPostProcessors;
- set up the message source and the event multicaster, and register the listeners;
- check the bean graph: a missing or ambiguous dependency, or a cycle, fails here, before any constructor runs;
- create every singleton that is not lazy;
- start the auto-startup SmartLifecycle beans;
- 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:
- The context is built from generated
BeanModules, not from classes or packages scanned at startup. refresh()checks the whole bean graph before any bean is created. Spring finds missing beans and cycles only while creating them.refreshAsync()andcloseAsync()wait for init and destroy callbacks that return aFuture. Spring has no asynchronous callbacks.getBean<T>({qualifier})replacesgetBean(name, Class), andgetBeanByName(name)replacesgetBean(String). Dart has no overloads.addListener<E>returns a registration handle to remove the listener.- Locales are strings such as
'de', notLocaleobjects.