zengbohan1 opened a new pull request, #16438:
URL: https://github.com/apache/dubbo/pull/16438

   ## 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
   
   ```java
   @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.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to