我有一个配置类,根据所选配置文件定义了两个 bean,并覆盖了配置方法:
@Configuration
class MyConfig {
@Profile("profile")
@Bean
MyBean myBean(MyBeanProperties properties) {
return new MyBean(properties);
}
@Profile("!profile")
@Bean
MyBean myBean(MyBeanProperties properties, AdditionalProperties addProps) {
MyBean result = new MyBean(properties);
result.addAdditionalProperties(addProps);
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
MyBean和一个自动装配到其中的类
@Service
class MyService {
MyBean autowiredBean;
private MyService(MyBean bean) { this.autowiredBean = bean; }
}
Run Code Online (Sandbox Code Playgroud)
现在,当我启动 Spring 上下文时,它失败并显示消息
com.example.MyServce 中构造函数的参数 0 需要类型为“com.example.MyBean”的 bean,但无法找到。
这怎么可能?我清楚地定义了 Spring bean,因此它应该在创建上下文时出现。
dan*_*niu 10
原因是 Spring 由于配置方法名称而认为这些 bean 具有相同的名称,因此无法实例化它们(尽管在任何给定的活动 Profile 中只应创建一个)。这会工作得很好:
@Configuration
class MyConfig {
@Profile("profile")
@Bean
MyBean myBean(MyBeanProperties properties) {
return new MyBean(properties);
}
@Profile("!profile")
@Bean
// note different method name
MyBean otherBean(MyBeanProperties properties, AdditionalProperties addProps) {
MyBean result = new MyBean(properties);
result.addAdditionalProperties(addProps);
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
我没有在任何地方找到这种行为的解释,所以我发布了这个自我回答的问题来分享。
我在现实生活中遇到过这种情况,WebClient它是通过一个配置文件中的客户端注册来实例化的,而另一个配置文件中没有客户端注册(因为创建交换过滤器不需要任何客户端注册)。
小智 6
这是因为当两个 bean 定义了相同的方法名称并且其中一个 bean 预计会根据某种条件(在本例中基于配置文件)被跳过时会导致这种情况。在本例中,“myBean”使用不同的配置文件定义了两次。
解析配置类的方式是迭代该类中的所有 beanMethods 并添加相应的 bean 定义。迭代是按照 beanMethods 在配置类中定义的顺序进行的。这是代码的链接。
根据这些 bean 在配置类中定义的顺序,如果根据配置文件注释预计将跳过定义的第一个 bean,则 beanMethod 名称将添加到“要跳过”beanMethod 的列表中。这是代码的链接。
现在,当它遇到第二个同名的 bean 时,它会发现该 beanMethod 名称已经存在于“要跳过的”方法列表中,因此即使没有固有条件(如配置文件)也会跳过该 bean这会导致它被跳过。这是代码的链接。
您会注意到,如果交换 Bean 的顺序并使用之前失败的相同配置文件来运行,该 Bean 将被拾取。
在配置类中使用唯一的 beanMethod 名称将是避免这种情况的最佳方法。
| 归档时间: |
|
| 查看次数: |
7222 次 |
| 最近记录: |