【问题标题】:Defining Spring Beans with same method name for different profiles为不同的配置文件定义具有相同方法名称的 Spring Bean
【发布时间】:2023-04-01 15:01:01
【问题描述】:

我有一个配置类,根据所选配置文件定义了两个 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;
    }
}

以及一个将MyBean 自动连接到其中的类

@Service
class MyService {
     MyBean autowiredBean;
     private MyService(MyBean bean) { this.autowiredBean = bean; }
}

现在,当我启动 Spring 上下文时,它会失败并显示消息

com.example.MyServce 中构造函数的参数 0 需要一个无法找到的“com.example.MyBean”类型的 bean。

这怎么可能?我清楚地定义了 Spring bean,因此它应该在创建上下文时出现。

【问题讨论】:

标签: java spring


【解决方案1】:

这样做的原因是 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;
    }
}

我没有在任何地方发现这种行为的解释,所以我发布了这个自我回答的问题以分享。

这发生在我的真实案例中,WebClient 在一个配置文件中使用客户端注册实例化,而在另一个配置文件中没有一个(因为创建交换过滤器不需要)。

【讨论】:

  • 遇到了完全相同的问题,非常感谢。这是非常奇怪的行为,可能没有记录,也没有在春季作为警告记录。看起来如果多个相同类型的方法具有相同的名称(与 args 的数量/类型无关),spring 会忽略那些 @beans。
【解决方案2】:

当两个 bean 使用相同的方法名定义并且其中一个会根据某些条件(在这种情况下基于配置文件)被跳过时,会导致这种情况。在这种情况下,“myBean”使用不同的配置文件定义了两次。

解析配置类的方式是遍历该类中的所有 beanMethods 并添加相应的 bean 定义。迭代是按照配置类中 beanMethods 的定义顺序进行的。 Here 是代码的链接。

根据配置类中定义这些 bean 的顺序,如果预计根据配置文件注释跳过定义的第一个 bean,则 beanMethod 名称将添加到“待跳过”列表中bean 方法。 Here 是代码的链接。

现在,当它遇到第二个同名 bean 时,它会发现这个 beanMethod 名称已经存在于“to-be-skipped”方法列表中,因此即使没有固有条件(如配置文件),这将导致它被跳过。 Here 是代码的链接。

您会注意到,如果您交换 bean 的顺序并使用之前失败的相同配置文件来运行,那么 bean 会被拾取。

在配置类中使用唯一的 beanMethod 名称将是避免这种情况的最佳方法。

【讨论】:

    【解决方案3】:

    我知道为什么更改方法的名称会允许加载应用程序上下文:

    问题是,spring 容器要求它的所有 bean 都有一个唯一的名称,如此处所述 https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-beanname

    每个 bean 都有一个或多个标识符。这些标识符在承载 bean 的容器中必须是唯一的。

    当不使用 XML 配置时,我认为拥有相同 bean 方法名称的唯一方法是在 @Bean(NAME) 注释中为 bean 赋予唯一名称,有关详细信息,请参阅此 Spring bean with same method name but different qualifier fail to load

    【讨论】:

    • 是的,但问题是为什么 Spring 尝试创建两个 bean,因为其中一个不在活动配置文件中。
    • 我认为它发生在检查期间而不是实际的类加载,如果检查失败,它会忽略那些 bean
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-10
    • 1970-01-01
    • 2023-04-10
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多