Skip to content

Conversation

@gnodet
Copy link
Contributor

@gnodet gnodet commented Nov 6, 2025

Overview

This PR introduces the @Aggregate annotation to Maven's dependency injection framework, enabling modular contribution to aggregated collections without replacing the entire collection.

Key Features

  • @aggregate annotation for @Provides methods that return List<T> or Map<String, T>
  • Multiple contributors: Allows multiple modules to contribute entries to the same collection
  • Preserves auto-aggregation: Maintains existing auto-aggregation behavior while enabling explicit contributions
  • Priority support: Supports priority-based ordering for List<T> aggregations

Behavior

  1. Without @aggregate: An explicit @Provides method for List<T> or Map<String, T> replaces the auto-aggregated collection entirely
  2. With @aggregate: The @Provides method contributes entries to the auto-aggregated collection, allowing multiple providers to coexist
  3. Mixed mode: If both @Aggregate and non-@Aggregate providers exist, the non-@Aggregate provider takes precedence

Use Cases

  • Plugin architecture: Different modules can register services without depending on a central registry
  • Lifecycle providers: Multiple modules can contribute lifecycle phases
  • Extensible service registries: Core and extension modules can contribute services to the same map/list

Implementation Details

  • New Binding.BindingToMethod class to track the source method for bindings
  • Enhanced InjectorImpl with aggregation logic for List and Map injection
  • Support for @Priority ordering in aggregated lists
  • Comprehensive test coverage with 30+ test cases

Example Usage

// Module A
@Provides
@Aggregate
Map<String, LifecycleProvider> lifecycleProviders() {
    return Map.of("clean", new CleanLifecycle());
}

// Module B
@Provides
@Aggregate
Map<String, LifecycleProvider> moreLifecycleProviders() {
    return Map.of("deploy", new DeployLifecycle());
}

// Consumer
@Inject
Map<String, LifecycleProvider> allLifecycles; // Contains all contributions

Files Changed

  • api/maven-api-di/src/main/java/org/apache/maven/api/di/Aggregate.java - New annotation with comprehensive documentation
  • impl/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java - Added BindingToMethod class
  • impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java - Aggregation logic implementation
  • impl/maven-di/src/main/java/org/apache/maven/di/impl/ReflectionUtils.java - Updated to use BindingToMethod
  • impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java - Comprehensive test coverage

Testing

Added 30+ test cases covering:

  • Basic map and list aggregation
  • Multiple @Aggregate providers
  • Explicit providers (with and without @Aggregate)
  • Priority ordering
  • Empty collections
  • Duplicate keys
  • Mixed scenarios
  • Integration with existing DI features

Pull Request opened by Augment Code with guidance from the PR author

This commit introduces the @aggregate annotation to Maven's dependency injection
framework, enabling modular contribution to aggregated collections without
replacing the entire collection.

Key Features:
- @aggregate annotation for @provides methods that return List<T> or Map<String, T>
- Allows multiple modules to contribute entries to the same collection
- Preserves auto-aggregation behavior while enabling explicit contributions
- Supports priority-based ordering for List<T> aggregations

Behavior:
1. Without @aggregate: An explicit @provides method for List<T> or Map<String, T>
   replaces the auto-aggregated collection entirely
2. With @aggregate: The @provides method contributes entries to the auto-aggregated
   collection, allowing multiple providers to coexist
3. Mixed mode: If both @aggregate and non-@aggregate providers exist, the
   non-@aggregate provider takes precedence

Use Cases:
- Plugin architecture: Different modules can register services without depending
  on a central registry
- Lifecycle providers: Multiple modules can contribute lifecycle phases
- Extensible service registries: Core and extension modules can contribute
  services to the same map/list

Implementation Details:
- New Binding.BindingToMethod class to track the source method for bindings
- Enhanced InjectorImpl with aggregation logic for List and Map injection
- Support for @priority ordering in aggregated lists
- Comprehensive test coverage with 30+ test cases

Examples:
  @provides
  @aggregate
  Map<String, LifecycleProvider> lifecycleProviders() {
      return Map.of("clean", new CleanLifecycle());
  }

  @Inject
  Map<String, LifecycleProvider> allLifecycles; // Contains all contributions
@gnodet gnodet added this to the 4.1.0 milestone Nov 6, 2025
@gnodet gnodet added the enhancement New feature or request label Nov 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant