Skip to content

[ISSUE #12637] Add reference bean naming strategy to avoid @Resource by-name conflicts - #16438

Open
zeng-bohan wants to merge 1 commit into
apache:3.3from
zeng-bohan:feat/reference-bean-naming-strategy
Open

zeng-bohan wants to merge 1 commit into
apache:3.3from
zeng-bohan:feat/reference-bean-naming-strategy

Conversation

@zeng-bohan

Copy link
Copy Markdown

Problem

When @DubboReference is used on a field, ReferenceAnnotationBeanPostProcessor registers a ReferenceBean whose bean name defaults to the field name (when no explicit id attribute is set). If another bean in the same application context declares a field with the same name but a different type and injects it by name (JSR-250 @Resource), the injection fails.

Reproduction

@RestController
public class Demo2Controller {
    @DubboReference
    private Demo2Service demo1Service;   // registers ReferenceBean 'demo1Service' of type Demo2Service
}

@RestController
public class Demo1Controller {
    @Resource
    private Demo1Service demo1Service;   // by-name lookup hits the Demo2Service proxy -> failure
}

Startup fails with:

Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException:
Bean named 'demo1Service' is expected to be of type 'com.example.dubbodemo.Demo1Service'
but was actually of type 'com.example.dubbodemo.Demo2ServiceDubboProxy0'

Root cause

This is a cross-annotation conflict with a timing gap:

  1. @DubboReference vs @DubboReference collisions on the same bean name are already handled by a rename fallback (xxx#2) inside registerReferenceBean.
  2. However, @Resource never registers a bean definition; it resolves by name at injection time, which happens after @DubboReference has already registered its ReferenceBean under the field name. So no rename/protection logic can kick in at that point.

Proposal

Add an opt-in naming strategy for reference beans that do not declare an explicit id, selected via a Spring environment property:

dubbo.application.reference-bean-naming-strategy=field-name | interface-name
  • field-name (default, current behavior): bean name = annotated field/setter property name.
  • interface-name (new): bean name = simple name of the referenced service interface, so it no longer depends on the declaring field name.

The default keeps full backward compatibility (non-breaking). An explicit id attribute always takes priority regardless of the strategy.

Verification

Reproduced and verified with a Spring Boot 2.7.13 + Dubbo 3.3.6 + JDK 17 consumer application (ZooKeeper registry) containing exactly the conflicting controllers above, with the patched class applied.

Scenario 1 - default strategy (field-name, property not set): behavior unchanged, still fails

[INFO] Register dubbo reference bean: demo1Service = ReferenceBean:com.example.dubbodemo.Demo2Service()
       at private com.example.dubbodemo.Demo2Service com.example.dubbodemo.Demo2Controller.demo1Service
...
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException:
Bean named 'demo1Service' is expected to be of type 'com.example.dubbodemo.Demo1Service'
but was actually of type 'com.example.dubbodemo.Demo2ServiceDubboProxy0'

[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0   (mvn test exit code 1)

Scenario 2 - -Ddubbo.application.reference-bean-naming-strategy=interface-name: application starts

[INFO] Register dubbo reference bean: Demo2Service = ReferenceBean:com.example.dubbodemo.Demo2Service()
       at private com.example.dubbodemo.Demo2Service com.example.dubbodemo.Demo2Controller.demo1Service
...
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0           (mvn test exit code 0)

The Demo2Controller reference bean is now registered as Demo2Service; Demo1Controller's @Resource lookup by name finds nothing named demo1Service, falls back to by-type matching and successfully injects the local Demo1ServiceImpl provider bean.

A new test ReferenceBeanNamingStrategyTest covers both strategies (default strategy derives names from the field name including the existing rename-to-#2 fallback; interface-name strategy derives them from the referenced interface simple names).

Note

If desired, maintainers could consider flipping the default value to interface-name in a future major/minor release (e.g. 3.4) after collecting feedback, since it removes a whole class of by-name conflicts.

@codecov-commenter

codecov-commenter commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.92%. Comparing base (df9c5e1) to head (0de30f1).

Additional details and impacted files
@@             Coverage Diff              @@
##                3.3   #16438      +/-   ##
============================================
+ Coverage     60.91%   60.92%   +0.01%     
- Complexity    11769    11783      +14     
============================================
  Files          1953     1953              
  Lines         89275    89287      +12     
  Branches      13474    13477       +3     
============================================
+ Hits          54382    54402      +20     
+ Misses        29312    29307       -5     
+ Partials       5581     5578       -3     
Flag Coverage Δ
integration-tests-java21 32.14% <38.46%> (+<0.01%) ⬆️
integration-tests-java8 32.21% <38.46%> (+<0.01%) ⬆️
samples-tests-java21 32.24% <38.46%> (+0.04%) ⬆️
samples-tests-java8 29.72% <38.46%> (-0.10%) ⬇️
unit-tests-java11 59.19% <100.00%> (+<0.01%) ⬆️
unit-tests-java17 58.64% <100.00%> (-0.01%) ⬇️
unit-tests-java21 58.66% <100.00%> (-0.03%) ⬇️
unit-tests-java25 58.61% <100.00%> (+0.01%) ⬆️
unit-tests-java8 59.19% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@zeng-bohan

Copy link
Copy Markdown
Author

Friendly bump after two weeks: CI is green across the board (30/30 checks, patch coverage at 100%), and the change is non-breaking by construction — dubbo.application.reference-bean-naming-strategy defaults to field-name, so existing behaviour is byte-identical unless the new interface-name value is opted into. This fixes the @Resource injection conflict tracked in #12637 (reproduced on 3.3.6). Would appreciate a review when someone has a moment — happy to adjust the design if the team prefers a different switch shape. Thanks!

…ource by-name conflicts

@dubboReference without an explicit id registers the ReferenceBean under
the annotated field name. A same-named field of a different type then
occupies that bean name, and JSR-250 @resource injections - which resolve
by name later in the lifecycle, after @DubboReference has registered -
fail with BeanNotOfRequiredTypeException (apache#12637).

Add an opt-in strategy selected via the Spring environment property
dubbo.application.reference-bean-naming-strategy:

- field-name (default): historical behavior, bean name = annotated
  field/setter property name; byte-identical unless opted out
- interface-name: bean name = simple name of the referenced service
  interface, so it no longer depends on the declaring field name

An explicit id attribute always takes priority regardless of the
strategy. Unknown or blank values fall back to field-name.

Signed-off-by: zengbohan1 <310902929+zengbohan1@users.noreply.github.com>
@zeng-bohan
zeng-bohan force-pushed the feat/reference-bean-naming-strategy branch from fb7b459 to 0de30f1 Compare September 15, 2026 06:08
@zeng-bohan

Copy link
Copy Markdown
Author

Housekeeping, no functional change: I squashed the branch to a single commit and rebased it onto the current 3.3 tip (df9c5e13). The net diff is byte-identical to fb7b459 except for the three files the rebase picks up from upstream (pom.xml, dubbo-plugin/dubbo-rest-openapi/pom.xml, NettyHttp3ConnectionClient.java) — none of the six new 3.3 commits touch dubbo-config-spring. CI is re-running on the new head 0de30f1e.

@zeng-bohan

Copy link
Copy Markdown
Author

@RainYuY @zrlw — could one of you take a first look at this one? It has been open three weeks with no review yet, and it is green across the board: 32/32 checks passing, patch coverage 100%, and no conflicts with the current 3.3 tip.

The change adds a reference-bean naming strategy so @Resource injection stops colliding by field name — the conflict tracked in #12637, reproduced on 3.3.6. dubbo.application.reference-bean-naming-strategy defaults to field-name, so existing behaviour is byte-identical unless interface-name is explicitly opted into; no migration or config change for current users.

Happy to adjust the switch shape, the default, or the naming if the team prefers something different. Thanks!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants