Skip to content

Prevent overriding of configuration classes with the same simple name in NamedContextFactory child contexts - #1738

Open
akenra wants to merge 1 commit into
spring-cloud:5.0.xfrom
akenra:gh-1359
Open

Prevent overriding of configuration classes with the same simple name in NamedContextFactory child contexts#1738
akenra wants to merge 1 commit into
spring-cloud:5.0.xfrom
akenra:gh-1359

Conversation

@akenra

@akenra akenra commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Context

NamedContextFactory registers configuration classes in the per-client child context via AnnotationConfigApplicationContext.register(), which names the configuration-class bean after its simple class name. When a client-specific configuration class shares its simple name with the default configuration - for example a custom class named LoadBalancerClientConfiguration in the user's own package, as reported in #1359 - both get registered under the same bean name, and the default configuration registered later silently replaces the custom definition. The custom configuration is ignored and its beans are missing from the child context.

Reproduction from the issue: take the feign-eureka custom-loadbalancer-config sample and rename LoadBalancerClientConfig to LoadBalancerClientConfiguration; the custom load balancer configuration stops being applied.

Note that Spring Framework 7.1's AnnotationBeanNameGenerator disambiguates nested classes, but top-level classes in different packages still collide.

What this PR does

  1. Registers configuration classes with fully qualified bean names. NamedContextFactory.registerBeans(...) now registers through an AnnotatedBeanDefinitionReader with an explicit ClassUtils.getQualifiedName(...)-based bean name for each configuration class, so distinct classes can no longer override each other in a child context. This fixes it for every NamedContextFactory consumer, not only load balancing.
  2. Keeps everything else about registration intact. Client-specific configurations are still registered first, default.* configurations and the default configuration type last; class-level conditions and common definition annotations (scope, lazy, primary, depends on, ...) are evaluated exactly as before, since registration still goes through AnnotatedBeanDefinitionReader; the fail-fast AnnotationConfigRegistry assertion is preserved.
  3. With both classes registered, the pattern from the issue works as designed: the client-specific configuration is processed first, and @ConditionalOnMissingBean in the default configuration backs off to the user's beans.

Backward compatibility

  • Beans produced by @Bean methods keep their names; only the configuration-class bean definitions themselves are renamed. All NamedContextFactory lookups (getInstance, getInstances, getAnnotatedInstance, providers) are type-based, as are all in-repo consumers.
  • Registering the same configuration class twice keeps the previous outcome (second registration replaces the first).
  • The AOT path behaves the same: LoadBalancerChildContextInitializer calls the same registerBeans(...), and the generated child-context initializers embed the (now fully qualified) configuration bean names consistently on both JVM and AOT paths.

Testing

  • New tests with top-level fixture configuration classes that deliberately share the simple name of their respective default configuration class: NamedContextFactoryTests.testClientConfigurationWithSameSimpleNameAsDefaultConfiguration in spring-cloud-context and LoadBalancerClientFactoryTests.shouldApplyClientConfigurationWithSameSimpleNameAsDefaultConfiguration in spring-cloud-loadbalancer. Both are red before the fix and green after; the load balancer test additionally asserts that the default configuration still applies.
  • Full module suites: spring-cloud-context 234 tests, spring-cloud-commons 305 tests, spring-cloud-loadbalancer 181 tests. One failure per context/loadbalancer suite is pre-existing and unrelated (RSA key PEM parsing, and a Windows-only Connection refused: getsockopt: message assertion); both reproduce identically on clean 5.0.x.

Fixes #1359

Notes for reviewers

  • Configuration-class beans in child contexts now carry fully qualified names. Nothing in this repo looks them up by name, but third-party code that fetched a configuration-class bean from a child context by simple name would need to switch to a type-based lookup.
  • The fixtures use top-level classes on purpose: nested classes already get collision-free bean names on Spring Framework 7.1, so the tests would not reproduce the issue otherwise.

… in NamedContextFactory child contexts

Configuration classes registered in a NamedContextFactory child context were named after their simple class name. As a result, a client-specific configuration class with the same simple name as the default configuration (e.g. a custom LoadBalancerClientConfiguration) was silently replaced by the default configuration registered later and its beans were ignored. Configuration classes are now registered with fully qualified bean names so that client-specific and default configuration classes can coexist in the child context and @ConditionalOnMissingBean in the default configuration can back off as intended.

Fixes spring-cloudgh-1359

Signed-off-by: akenra <37288280+akenra@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants