@Scope

@Scope(...) sets how many instances of a bean exist and how long each one lives.

Why

Without @Scope a bean is a singleton: one instance for the whole context, created at startup and shared by everyone who injects it. That is right for most services.

Choose another scope when a bean holds state that must not be shared:

Shorter scopes inside singletons

A singleton keeps what it was injected with. A screen-scoped bean injected directly into a singleton would stay the first screen's instance forever (the container logs a warning). With proxyMode: ScopedProxyMode.targetClass (or interfaces) the singleton gets a generated proxy instead. Every call on the proxy asks the scope for the current instance. An ObjectProvider is the other way to reach the current instance.

The proxied type must be implementable from generated code: an interface, or a class without private members, not generic.

Example

import 'package:spring_core_dart/spring_core_dart.dart';

// Each user of a cart gets their own instance.
@Component()
@Scope(ScopeType.prototype)
class ShoppingCart {
  final items = <String>[];
}

// One instance per open screen. Services see the current screen's
// state through a generated proxy.
@Component()
@Scope('screen', proxyMode: ScopedProxyMode.targetClass)
class ScreenState {
  String title = '';

  @PreDestroy()
  void clear() => title = '';
}

@Service()
class Analytics {
  Analytics(this.state); // the proxy, not a fixed instance
  final ScreenState state;

  String currentScreen() => state.title;
}

Registering the custom scope and ending it:

// SimpleMapScope keeps instances in a map until destroy() is called.
final screen = SimpleMapScope(conversationId: 'checkout');

final context = AnnotationConfigApplicationContext([appBeans]);
context.beanFactory.registerScope('screen', screen); // before refresh
context.refresh();

context.getBean<ScreenState>().title = 'Checkout';
// ... when the screen closes:
screen.destroy(); // runs @PreDestroy of ScreenState; next lookup is fresh

A scope of your own implements CustomScope (get, remove, registerDestructionCallback, conversationId).

Parameters

Name Type Default Meaning
value (positional) String required The scope name: ScopeType.singleton, ScopeType.prototype or the name of a registered custom scope.
proxyMode ScopedProxyMode ScopedProxyMode.no no injects the instance directly. interfaces and targetClass inject a generated proxy that asks the scope on every call.

Related types:

Type Meaning
ScopeType.singleton / ScopeType.prototype The names of the two built-in scopes.
ScopedProxyMode no, interfaces, targetClass.
CustomScope The interface of a custom scope (Spring's Scope).
SimpleMapScope A ready-made custom scope backed by a map, emptied by destroy().

In Spring (Java)

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
class ShoppingCart { ... }

@Component
@Scope(value = "screen", proxyMode = ScopedProxyMode.TARGET_CLASS)
class ScreenState { ... }

beanFactory.registerScope("screen", new MyScreenScope());

Differences that matter:

See also