Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BeanNotOfRequiredTypeException when using spring bean injection #12637

Open
aishang1993 opened this issue Jul 1, 2023 · 11 comments
Open

BeanNotOfRequiredTypeException when using spring bean injection #12637

aishang1993 opened this issue Jul 1, 2023 · 11 comments
Labels
component/sdk Related with apache/dubbo good first issue Good for starters to contribute help wanted Everything needs help from contributors type/enhancement Everything related with code enhancement or performance

Comments

@aishang1993
Copy link

  • Dubbo version: 3.2.2
  • Java version: 1.8
  • SpringBoot: 2.7.13

前面也讨论过这个问题

3.X ReferenceAnnotationBeanPostProcessor 按@DubboReference属性名注册别名
image
image
这样会导致在不同的类中注入不同类型的Bean时必须要用不同的属性名称,如果像下面这样就会导致异常
image
image
image
dubbo2.x 就没有这个问题,3.0中使用属性名注册别名是有什么特殊的用意吗?
我理解这与spring并没有关系 https://github.com/apache/dubbo/issues/10443#issuecomment-1212703407,
因为我只要不用 dubbo3.x 就不会导致异常。
@chickenlj

@aishang1993 aishang1993 added the type/discussion Everything related with code discussion or question label Jul 1, 2023
@liufeiyu1002
Copy link
Contributor

可以提供一个能够复现异常的 demo 么?我这可以正常注入。 使用属性名注册别名 是因为已经有相同类型的bean 被注入到了容器中 且 beanName 不同 才会注册别名

@aishang1993
Copy link
Author

aishang1993 commented Jul 1, 2023

可以提供一个能够复现异常的 demo 么?我这可以正常注入。 使用属性名注册别名 是因为已经有相同类型的bean 被注入到了容器中 且 beanName 不同 才会注册别名

demo,这是我复现的demo,Demo2Controller中的demo1Service会导致异常,修改属性名为demo2Service就没问题。

@liufeiyu1002
Copy link
Contributor

可以提供一个能够复现异常的 demo 么?我这可以正常注入。 使用属性名注册别名 是因为已经有相同类型的bean 被注入到了容器中 且 beanName 不同 才会注册别名

demo,这是我复现的demo,Demo2Controller中的demo1Service会导致异常,修改属性名为demo2Service就没问题。

@Resource 是 先按照name 查找,找不到再按照类型 查找, 按照你demo 中 Demo1ServiceImpl@service 注入容器以后 对应的key 为 demo1ServiceImpl@DubboReference Demo2Service demo1Service; 注入容器中时 发现 没有 key demo1Service 的类型 会使用当前key。 容器执行到属性注入的时候 @Resource 注入的时候 先按照 name 查询,查找到了Demo2Service 类型注入的那个 bean 导致 赋值的时候 类型不一致报错

  1. @autowire 注解替换 @resource
  2. 显示的 指定 service name
@Service("demo1Service")
public class Demo1ServiceImpl implements Demo1Service

同理 不使用dubbo 注解的时候 如果 demo1Service,demo2Service实现声明为

@Service("demo1Service")
public class Demo1ServiceImpl implements Demo1Service
@Service
public class Demo2ServiceImpl implements Demo2Service

同时 按照

    @Resource
    private Demo1Service demo1Service;
    @Resource
    private Demo2Service demo1Service;

注入也会类型异常
在开发时候 注意规范命名应该可以很大程度避免这种错误

@aishang1993
Copy link
Author

可以提供一个能够复现异常的 demo 么?我这可以正常注入。 使用属性名注册别名 是因为已经有相同类型的bean 被注入到了容器中 且 beanName 不同 才会注册别名

demo,这是我复现的demo,Demo2Controller中的demo1Service会导致异常,修改属性名为demo2Service就没问题。

@Resource 是 先按照name 查找,找不到再按照类型 查找, 按照你demo 中 Demo1ServiceImpl@service 注入容器以后 对应的key 为 demo1ServiceImpl@DubboReference Demo2Service demo1Service; 注入容器中时 发现 没有 key demo1Service 的类型 会使用当前key。 容器执行到属性注入的时候 @Resource 注入的时候 先按照 name 查询,查找到了Demo2Service 类型注入的那个 bean 导致 赋值的时候 类型不一致报错

  1. @autowire 注解替换 @resource
  2. 显示的 指定 service name
@Service("demo1Service")
public class Demo1ServiceImpl implements Demo1Service

同理 不使用dubbo 注解的时候 如果 demo1Service,demo2Service实现声明为

@Service("demo1Service")
public class Demo1ServiceImpl implements Demo1Service
@Service
public class Demo2ServiceImpl implements Demo2Service

同时 按照

    @Resource
    private Demo1Service demo1Service;
    @Resource
    private Demo2Service demo1Service;

注入也会类型异常 在开发时候 注意规范命名应该可以很大程度避免这种错误

这个逻辑能理解,但我还是更喜欢2.x那样不会根据属性名去注册别名,这样2.x到3.x升级也会更方便。谢谢你的解答!!!

@AlbumenJ
Copy link
Member

AlbumenJ commented Jul 3, 2023

Related with #12474

@AlbumenJ
Copy link
Member

AlbumenJ commented Jul 3, 2023

可以提供一个能够复现异常的 demo 么?我这可以正常注入。 使用属性名注册别名 是因为已经有相同类型的bean 被注入到了容器中 且 beanName 不同 才会注册别名

demo,这是我复现的demo,Demo2Controller中的demo1Service会导致异常,修改属性名为demo2Service就没问题。

@Resource 是 先按照name 查找,找不到再按照类型 查找, 按照你demo 中 Demo1ServiceImpl@service 注入容器以后 对应的key 为 demo1ServiceImpl@DubboReference Demo2Service demo1Service; 注入容器中时 发现 没有 key demo1Service 的类型 会使用当前key。 容器执行到属性注入的时候 @Resource 注入的时候 先按照 name 查询,查找到了Demo2Service 类型注入的那个 bean 导致 赋值的时候 类型不一致报错

  1. @autowire 注解替换 @resource
  2. 显示的 指定 service name
@Service("demo1Service")
public class Demo1ServiceImpl implements Demo1Service

同理 不使用dubbo 注解的时候 如果 demo1Service,demo2Service实现声明为

@Service("demo1Service")
public class Demo1ServiceImpl implements Demo1Service
@Service
public class Demo2ServiceImpl implements Demo2Service

同时 按照

    @Resource
    private Demo1Service demo1Service;
    @Resource
    private Demo2Service demo1Service;

注入也会类型异常 在开发时候 注意规范命名应该可以很大程度避免这种错误

这个逻辑能理解,但我还是更喜欢2.x那样不会根据属性名去注册别名,这样2.x到3.x升级也会更方便。谢谢你的解答!!!

可以试一下 3.2.3 版本是不是还有问题

@aishang1993
Copy link
Author

可以提供一个能够复现异常的 demo 么?我这可以正常注入。 使用属性名注册别名 是因为已经有相同类型的bean 被注入到了容器中 且 beanName 不同 才会注册别名

demo,这是我复现的demo,Demo2Controller中的demo1Service会导致异常,修改属性名为demo2Service就没问题。

@Resource 是 先按照name 查找,找不到再按照类型 查找, 按照你demo 中 Demo1ServiceImpl@service 注入容器以后 对应的key 为 demo1ServiceImpl@DubboReference Demo2Service demo1Service; 注入容器中时 发现 没有 key demo1Service 的类型 会使用当前key。 容器执行到属性注入的时候 @Resource 注入的时候 先按照 name 查询,查找到了Demo2Service 类型注入的那个 bean 导致 赋值的时候 类型不一致报错

  1. @autowire 注解替换 @resource
  2. 显示的 指定 service name
@Service("demo1Service")
public class Demo1ServiceImpl implements Demo1Service

同理 不使用dubbo 注解的时候 如果 demo1Service,demo2Service实现声明为

@Service("demo1Service")
public class Demo1ServiceImpl implements Demo1Service
@Service
public class Demo2ServiceImpl implements Demo2Service

同时 按照

    @Resource
    private Demo1Service demo1Service;
    @Resource
    private Demo2Service demo1Service;

注入也会类型异常 在开发时候 注意规范命名应该可以很大程度避免这种错误

这个逻辑能理解,但我还是更喜欢2.x那样不会根据属性名去注册别名,这样2.x到3.x升级也会更方便。谢谢你的解答!!!

可以试一下 3.2.3 版本是不是还有问题

如果不同类型的成员变量使用相同的属性命名,在3.2.3 版本的异常信息变得准确了。

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'

@AlbumenJ AlbumenJ added help wanted Everything needs help from contributors type/enhancement Everything related with code enhancement or performance good first issue Good for starters to contribute component/sdk Related with apache/dubbo and removed type/discussion Everything related with code discussion or question labels Mar 11, 2024
@AlbumenJ AlbumenJ changed the title 3.X ReferenceAnnotationBeanPostProcessor 按@DubboReference属性名注册别名导致异常 BeanNotOfRequiredTypeException when using spring bean injection Mar 11, 2024
@JerryOops
Copy link

@AlbumenJ Hi, I have reproduced this bug in both Dubbo 3.2.2 & 3.2.3, so I'm guessing it's still pending to be fixed. If so, I am interested in solving this issue, please assign it to me :)

@JerryOops
Copy link

I think I have found what went wrong after finishing several breakpoint tests. Currently, dubbo will register the instance (of the field that is annotated with @DubboReference) into the IoC container, and it uses the field's name as the bean key by default. For example:

public class ControllerA {
     @DubboReference
     public TestService testService;
}

In the above code, dubbo will register "testService" --> [TestService bean] into the IoC container. This usually works fine, but not for the case when some other places in the project use some code like this:

public class ControllerB {
    @Resource
    public Test2Service testService;  // same field name
}

As @liufeiyu1002 said, @ Resource will enable Spring to look for a bean instance in the IoC container using the field name. Which means, Spring will detect the existence of "testService" --> [TestService bean], find out that TestService (existed bean's class) and Test2Service (the class of the field currently being injected) do not match. That is where an exception is thrown.

@JerryOops
Copy link

Experiment

To try and find a way to fix this issue, I did an experiment. In the following code, the two fields have difference classes but same field names, and they both use @DubboReference for the dependency injection.

public class ControllerA {
    @DubboReference 
    public TestService testService;
}
public class ControllerB {
    @DubboReference 
    public Test2Service testService;   // difference class, same field name
}

And dubbo can handle this situation. The first generated bean (assuming it's TestService) is "testService" -> [TestService bean], and the second generated bean is "testService#2" -> [Test2Service bean]. Dubbo slightly changes the latter's bean key to avoid conflicts.
image
image

My idea to solve this problem

So basically, my idea to resolve this issue is that: when we register @DubboReference-annotated bean into Spring IoC container, we shall attach the serial number after its bean key (no matter if it's the first time or not). Applying such an idea to the above code, it will be "testService#1" -> [TestService bean] and "testService#2" -> [Test2Service bean].
It probably should be resolving the problems occurred described by @aishang1993 , because same names of fields may still seem possible, but there is almost no possibility that a field named "XXX#1" will appear in an industry-level project.

Since I am new to the community, I may have overlooked certain important details. Please kindly share your opinions on whether this solution is workable, thanks!

@huogithub
Copy link

对比了下2.7.X与3.X org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor#registerReferenceBean 实现
同一JVM针对一个接口既是服务提供者又是消费者处理不一样
2.7.X向spring只注册了服务提供者实例,消费者只注册了别名;
3.X向spring注册了服务提供者实例,也注册了消费者实例,这样在这个容器Reference,Resource,autowired 注解混用存在很多问题;
能否参照2.7.X,只注册一个实例,消费者注册别名

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/sdk Related with apache/dubbo good first issue Good for starters to contribute help wanted Everything needs help from contributors type/enhancement Everything related with code enhancement or performance
Projects
Status: No status
Development

No branches or pull requests

5 participants