Skip to content

Conversation

@graemerocher
Copy link
Contributor

@graemerocher graemerocher commented Feb 9, 2026

Fixes #11703

AI was used in the production for this PR.

Summary

  • Introduces a configurable strategy for handling duplicate config files on the classpath (e.g. application.yml, application.properties).
  • Changes the default behavior to fail fast with a clear ConfigurationException listing all conflicting resource locations.
  • Adds options to keep prior behavior (FIRST_MATCH) or MERGE_ALL duplicate resources (with optional mergeOrder by jar name patterns).

Key changes

  • New API: ConfigurationLoadStrategy / ConfigurationLoadStrategyType
  • New builder hook: ApplicationContextBuilder#configurationLoadingStrategy(...) (propagated through Micronaut)
  • Environment loading updates to detect duplicates and apply the selected strategy
  • Tests covering default fail-fast, FIRST_MATCH, MERGE_ALL (+ mergeOrder), and env-specific resources
  • Docs updates + breaking change note (v5) using snippet-backed examples

Migration / behavior change

If you relied on “first match wins” when duplicates exist, configure:

ApplicationContext.builder()
    .configurationLoadingStrategy(s -> s.type(ConfigurationLoadStrategyType.FIRST_MATCH))
    .start();
  • Expose configuration load strategy on builders
  • Fix constant property source loader initialization
  • Handle duplicate configuration resources
  • Add tests for configuration loading strategy
  • Document duplicate configuration resource handling

Introduce ConfigurationLoadStrategy and ConfigurationLoadStrategyType to configure how duplicate configuration resources are handled.
Allow configuring the ConfigurationLoadStrategy via ApplicationContextBuilder and propagate it through DefaultApplicationContextBuilder and Micronaut.
Resolve constant property sources dynamically so default loaders reflect StaticOptimizations changes in tests and early environment construction.
Add configurable strategies (fail fast, first match, merge all) when the same application configuration resource appears multiple times on the classpath.
Cover default fail-fast behavior and the FIRST_MATCH and MERGE_ALL strategies, including mergeOrder and environment-specific duplicates.
Document the new default fail-fast behavior and how to configure FIRST_MATCH or MERGE_ALL using snippet-backed examples.
@graemerocher graemerocher added the type: improvement A minor improvement to an existing feature label Feb 9, 2026
@graemerocher graemerocher moved this to In review in 5.0.0-M1 Feb 9, 2026
@graemerocher graemerocher requested a review from Copilot February 9, 2026 11:18
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a configurable configuration-resource loading strategy to Micronaut so duplicate config files on the classpath can either fail fast (new default), fall back to first-match behavior, or be merged deterministically (optionally with merge ordering).

Changes:

  • Introduces ConfigurationLoadStrategy / ConfigurationLoadStrategyType and exposes it via ApplicationContextBuilder (and Micronaut) to control duplicate handling.
  • Updates DefaultEnvironment config loading to detect duplicates, optionally warn, fail fast, or merge resources (with optional regex-based mergeOrder).
  • Adds docs snippets + guide updates and new Spock tests for the new behavior.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test-suite/src/test/java/io/micronaut/docs/context/env/ConfigurationLoadStrategySnippet.java Java docs snippet demonstrating strategy configuration
test-suite-kotlin/src/test/kotlin/io/micronaut/docs/context/env/ConfigurationLoadStrategySnippet.kt Kotlin docs snippet demonstrating strategy configuration
test-suite-groovy/src/test/groovy/io/micronaut/docs/context/env/ConfigurationLoadStrategySnippet.groovy Groovy docs snippet demonstrating strategy configuration
test-suite-property-source/src/test/groovy/io/micronaut/context/env/ConstantPropertySourceSpec.groovy Updates test setup to explicitly restore legacy FIRST_MATCH behavior
test-suite-property-source/src/test/groovy/io/micronaut/context/env/ConfigurationLoadStrategySpec.groovy New tests for default fail-fast, FIRST_MATCH, MERGE_ALL, mergeOrder, env-specific duplicates
src/main/docs/guide/config/propertySource.adoc Documents duplicate configuration resource handling and strategy options
src/main/docs/guide/appendix/breaks.adoc Adds Micronaut 5 breaking change note for default fail-fast behavior
inject/src/main/java/io/micronaut/context/env/DefaultEnvironment.java Implements duplicate detection + strategy application during config loading
inject/src/main/java/io/micronaut/context/env/ConstantPropertySourceLoader.java Adjusts constant property source loader initialization behavior
inject/src/main/java/io/micronaut/context/env/ConfigurationLoadStrategyType.java New enum defining strategy types
inject/src/main/java/io/micronaut/context/env/ConfigurationLoadStrategy.java New record + builder defining strategy configuration
inject/src/main/java/io/micronaut/context/DefaultApplicationContextBuilder.java Stores/applies strategy configured via builder hook
inject/src/main/java/io/micronaut/context/ApplicationContextConfiguration.java Adds strategy to context configuration API with default
inject/src/main/java/io/micronaut/context/ApplicationContextBuilder.java Adds builder hook for configuring strategy
context/src/main/java/io/micronaut/runtime/Micronaut.java Propagates new builder hook through Micronaut

@graemerocher
Copy link
Contributor Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link
Contributor

Copilot AI commented Feb 9, 2026

@graemerocher I've opened a new pull request, #12384, to work on those changes. Once the pull request is ready, I'll request review from you.

…ing (#12384)

* Fix ConfigurationLoadStrategy to always defensively copy mergeOrder

Co-authored-by: graemerocher <[email protected]>

* Optimize performance for FIRST_MATCH with no warnings

Co-authored-by: graemerocher <[email protected]>

* Add cleanup for temp directories/JARs in tests

Co-authored-by: graemerocher <[email protected]>

* Add cross-extension duplicate detection in DefaultEnvironment

Co-authored-by: graemerocher <[email protected]>

* Address code review feedback and add documentation

Co-authored-by: graemerocher <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: graemerocher <[email protected]>
@cla-assistant
Copy link

cla-assistant bot commented Feb 9, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ graemerocher
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

@graemerocher
Copy link
Contributor Author

the test failure is unrelated

@dstepanov
Copy link
Contributor

I would prefer for this to be implemented in the same way as Spring has it with classpath*:

classpath: - Locates only the first resource found at the specified path within the classpath.

classpath*: - Locates all resources that match the given name across all classpath entries (directories and JARs).

And this can be used with .overrideConfigLocations("classpath*:/", "file:config/").
The logic should be put somewhere in DefaultClassPathResourceLoader with something like Stream<URL> getResources(String name, boolean includeAll);

@graemerocher
Copy link
Contributor Author

I don't understand, in this case the user doesn't specify where or how config is loaded. If there are duplicate application.properties it is unknown which is loaded which causes weird behaviour like having to search through your jar files for the duplicate. We need a solution to this in 5.x that is easier to debug

Copy link
Contributor

@sdelamo sdelamo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the feature is useful but the PR does not contain tests for the new logic in DefaultEnvironment::loadPropertySourceFromAbstractLoader

@graemerocher graemerocher requested a review from sdelamo February 11, 2026 07:32
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
51.8% Coverage on New Code (required ≥ 70%)
3 New Bugs (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Handle non-conforming ResourceLoader#getResources implementations and avoid assigning null under NullMarked in ConfigurationLoadStrategy.Builder.
Cover MERGE_ALL merging and mergeOrder behavior, invalid mergeOrder patterns, and FAIL_ON_DUPLICATE duplicates within the same extension. Add a context-level test to exercise Micronaut fluent configurationLoadingStrategy.
Address Sonar Groovy convention findings.
return propertySourceLoader.loadEnv(name, resourceLoader, activeEnvironment);
}

private Optional<PropertySource> loadPropertySourceFromAbstractLoader(String fileName,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move all of it into a new method to ClassPathResourceLoader with an option to merge of fail? I don't think it should be included here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand, ClassPathResourceLoader is in the core module and has nothing to do with environment configuration. Why would it go there?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the main issue is about the classpath files being loaded and the strategies resolving the conflicts etc. I don't think the DefaultEnvironment should be responsible for that logic. Considering there might be different consumers of the classpath files that might also want to react to similar problems. One of them being reported is actually the database flyway migrations being in multiple jars. Not sure if it's mostly about the properties or if some of the frameworks what to load multiple different files/configurations of the same name from the classpath.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: improvement A minor improvement to an existing feature

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

application.yml from Libraries Can Override or Be Silently Ignored — No Merging or Warning

4 participants