
# ObjectProvider

`ObjectProvider<T>` is a handle on the beans of `T` that looks them up only when asked: one bean, the bean if there is one, or all of them.

## Why

Inject `ObjectProvider<T>` instead of `T` when:

- the bean should be created later, or maybe never;
- the bean may be missing: `getIfAvailable` returns `null` or a default;
- there may be several beans and you want each of them, in order;
- the bean is a prototype or a scoped bean, and you want the current instance on every use;
- two beans depend on each other. A provider does not count as a dependency for cycle detection, so it breaks the cycle.

An injected provider resolves like the bean's other injection points. The bean itself, and the beans of its own `@Bean` methods, are left out. The parameter or field name selects among several candidates. Only autowire candidates take part. The beans it returns are recorded as dependencies, so they are destroyed after the bean that holds the provider. A `@Primary` decorator can therefore reach the other beans of its own type through a provider.

The provider of `getBeanProvider<T>()` is a plain lookup: `getObject()` works like `getBean<T>()`, and iteration includes every bean of `T`, also from parent factories.

An `ObjectProvider` is an `Iterable<T>`. Iterating creates the matching beans one by one, in registration order. `orderedList()` sorts them. `isEmpty`, `length` and `toString` create no beans.

For a single bean that is always there but should be resolved late, [@Lazy](lazy.md) on the injection point is an alternative: it injects a proxy of the type itself.

## Example

```dart
import 'package:spring_core_dart/spring_core_dart.dart';

abstract interface class Exporter {
  void run();
}

abstract interface class Clock {
  DateTime now();
}

class SystemClock implements Clock {
  @override
  DateTime now() => DateTime.now();
}

class Session {
  void print() {}
}

@Service()
class Reports {
  Reports(this.exporters, this.clocks, this.sessions);

  // Exporters are optional plug-ins: there may be none, one or several.
  final ObjectProvider<Exporter> exporters;
  // A configured clock, or the system clock.
  final ObjectProvider<Clock> clocks;
  // A prototype or scoped bean: the current instance on every call.
  final ObjectProvider<Session> sessions;

  void exportFirst() => exporters.ifAvailable((e) => e.run());

  void exportAll() {
    for (final exporter in exporters.orderedList()) {
      exporter.run();
    }
  }

  DateTime now() => clocks.getIfAvailable(SystemClock.new)!.now();

  void printSession() => sessions.getObject().print();
}
```

Outside a bean:

```dart
final greeter = context.getBeanProvider<Greeter>().getIfUnique();
final all = context.getBeanProvider<Greeter>().orderedList();
```

## Members

| Method | Meaning |
|---|---|
| `T getObject()` | The matching bean, created if needed. Throws `NoSuchBeanDefinitionException` when none matches and `NoUniqueBeanDefinitionException` when several match and none wins. |
| `T get()` | The same as `getObject()`, under the name of JSR-330's `Provider.get`. |
| `T? getIfAvailable([T Function()? defaultSupplier])` | The matching bean; when none matches, the supplier's result, or `null`. A bean whose scope is not active counts as missing. Still throws when several match and none wins. |
| `T? getIfUnique([T Function()? defaultSupplier])` | The matching bean; the supplier's result, or `null`, when none or several match. Never throws for those. |
| `void ifAvailable(void Function(T bean) consumer)` | Calls `consumer` when `getIfAvailable` finds a bean. |
| `void ifUnique(void Function(T bean) consumer)` | Calls `consumer` when `getIfUnique` finds a bean. |
| `List<T> orderedList()` | Every matching bean, sorted: `PriorityOrdered` first, then by `Ordered.order`, `@Order` or `@Priority`, then by registration. Empty when none match. |
| `Iterable<T>` members | `forEach`, `map`, `where`, `for (... in provider)`: every matching bean, in registration order. |

When several beans match, `getObject()` and `getIfAvailable()` choose like injection does: the single `@Primary` bean, else the only one not marked `@Fallback`, else the one whose name matches.

`ObjectFactory<T>` is the smallest form, with only `getObject()`. An `ObjectFactory<T>` injection point receives an `ObjectProvider<T>`. Prefer `ObjectProvider` in new code. A jakarta `Provider<T>` injection point receives one too.

## In Spring (Java)

```java
@Service
class Reports {
  private final ObjectProvider<Exporter> exporters;

  Reports(ObjectProvider<Exporter> exporters) {
    this.exporters = exporters;
  }

  void exportFirst() { exporters.ifAvailable(Exporter::run); }
  void exportAll() { exporters.orderedStream().forEach(Exporter::run); }
}
```

Differences that matter:

- `stream()` is the `Iterable` itself, and `orderedStream()` is `orderedList()`.
- `getObject(Object... args)` does not exist: constructor arguments cannot be passed at lookup.
- The default of `getIfAvailable` is an optional positional function, where Spring takes a `Supplier`.

## See also

- [@Lazy](lazy.md)
- [@Autowired](autowired.md)
- [Jakarta annotations](jakarta.md) (`Provider`)
- [@Lookup](lookup.md)
- [@Scope](scope.md)
- [@Primary](primary.md), [@Fallback](fallback.md), [@Qualifier](qualifier.md)
- [ApplicationContext](application-context.md) (`getBeanProvider`)
