
# @Conditional

`@Conditional([...])` registers a bean only when every listed `Condition` matches.

## Why

Use it when the rule is not a profile: a property combination, a file present, another bean missing, the platform. The conditions run while the bean definitions load, before any bean is created. When one does not match, the bean is not registered at all, as if it had no annotation.

A condition is a class that implements `Condition`:

```dart
abstract interface class Condition {
  bool matches(ConditionContext context);
}
```

`ConditionContext` gives access to:

| Member | What it is |
|---|---|
| `environment` | Properties (including `@PropertySource` files loaded so far) and active profiles. |
| `registry` | The bean definitions registered so far. |
| `beanFactory` | The same factory, for questions by type (`getBeanNamesForType`). |
| `resourceLoader` | Finds files, for a condition that depends on one. |
| `definition` | The bean definition being decided, with its `annotations`; `null` for a `@PropertySource`. |

A condition must not create beans. It only sees the definitions registered before it.

There are three ways to attach a condition:

1. `@Conditional([MyCondition])`: the class needs a no-argument constructor.
2. A const annotation class that implements `Condition`. It is evaluated directly, so it can take arguments. Spring Boot's `@ConditionalOnProperty` works this way.
3. A subclass of `Conditional` that names a set of conditions once.

For conditions on other beans, implement `ConfigurationCondition` and choose `ConfigurationPhase.registerBean`. The condition is then decided after the earlier classes have registered their beans.

## Example

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

// A rule without arguments, used through @Conditional.
class OnAuditEnabled implements Condition {
  const OnAuditEnabled();

  @override
  bool matches(ConditionContext context) =>
      context.environment.getProperty('audit.enabled') == 'true';
}

// Only when `audit.enabled=true` is configured.
@Component()
@Conditional([OnAuditEnabled])
class AuditTrail {}

// A rule with an argument: a const class that implements Condition,
// used directly as an annotation.
class OnProperty implements Condition {
  const OnProperty(this.name, {this.havingValue = 'true'});

  final String name;
  final String havingValue;

  @override
  bool matches(ConditionContext context) =>
      context.environment.getProperty(name) == havingValue;
}

@Component()
@OnProperty('feature.export')
class ExportFeature {}

// Registered only when no other bean named `cache` was declared before.
class OnMissingCache implements Condition {
  const OnMissingCache();

  @override
  bool matches(ConditionContext context) =>
      !context.registry.containsBeanDefinition('cache');
}

class MemoryCache {
  final entries = <String, Object>{};
}

@Configuration()
class CacheConfig {
  @Bean()
  @Conditional([OnMissingCache])
  MemoryCache cache() => MemoryCache();
}
```

## Parameters

| Name | Type | Default | Meaning |
|---|---|---|---|
| `value` (positional) | `List<Type>` | required | `Condition` implementations that all have to match. Each needs a no-argument constructor. |

## In Spring (Java)

```java
class OnAuditEnabled implements Condition {
  public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata m) {
    return "true".equals(ctx.getEnvironment().getProperty("audit.enabled"));
  }
}

@Component
@Conditional(OnAuditEnabled.class)
class AuditTrail {}
```

Differences that matter:

- `matches` has no `AnnotatedTypeMetadata` parameter. An annotation-style condition reads its own fields; another one reads `context.definition?.annotations`.
- A condition with arguments is simply a const class implementing `Condition`, not a meta-annotated annotation.

## See also

- [@Profile](profile.md)
- [@Import](import.md)
- [@Configuration](configuration.md)
- [BeanRegistrar](bean-registrar.md)
- [Environment](../config/environment.md)
