【问题标题】:How to solve a double bean conflict in SpringSpring中如何解决双bean冲突
【发布时间】:2021-02-25 19:41:18
【问题描述】:

我正在尝试同时使用两个库,GraphQL 和 Jmix。

我使用 Intellij 的新项目向导(安装了 Jmix 插件)创建了 Jmix 项目,然后使用标准的 graphql-spring-boot-starter 将 GraphQL 添加到 Gradle。然后我写了一个模式和解析器 bean。

但是在启动过程中,由于 WebSocket 端点 /subscriptions 在 Tomcat 上注册了两次,因此引发了异常。 (我尝试使用应用程序属性 graphql.servlet.subscriptions.websocket.path 更改端点,但这不是问题。)

经过一番挖掘,我发现 graphql-spring-boot-autoconfigure 中的 GraphQLWebsocketAutoConfiguration 类和 jmix-ui-starter 中的 VaadinAutoConfiguration 都注册了一个 ServerEndpointExporter bean,这是不应该发生的。

这是graphql的代码:

@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(ServerContainer.class)
public ServerEndpointExporter serverEndpointExporter() {
   return new ServerEndpointExporter();
}

这里是 Jmix 的:

@Bean
public ServerEndpointExporter websocketEndpointDeployer() {
    return new VaadinWebsocketEndpointExporter();
}

GraphQL 被标记为 ConditionalOnMissingBean,但在另一个之前注册,因此不会触发条件。

如何禁用这两个 bean 之一,或设置它们的优先级?

我设法通过完全禁用 GraphQL 的 websocket 服务来解决这个问题:

graphql.servlet.websocket.enabled = false

但我想知道一般如何解决这类问题。

【问题讨论】:

  • 能否请您提供指向该问题的 GraphQLWebAutoConfiguration 和 VaadinAutoConfiguration 的源代码的链接?
  • @MarkBramnik 我已经更新了库(我没有使用最新版本)并添加了链接。
  • 谢谢,请看我的回答。我知道这听起来像是解决方法,但在这种情况下,这是我能建议的最好的方法......

标签: spring spring-boot dependency-injection


【解决方案1】:

不幸的是,配置看起来有问题,所以没有什么可以像 Spring 那样“惯用地”做的。

我能想到的所有解决方案都是变通方法,解决此问题的最佳方法可能是在相应团队中打开一个缺陷,以便他们可以引入一个允许以两种方式禁用配置的属性。好吧,也许他们已经这样做了,谁知道呢。

至于可能的解决方案:

  1. 使用属性:graphql.servlet.websocket.enabled=false 它将禁用 graphql 配置。而且您将能够自己重新定义您需要的豆子。例如,如果需要,仅定义:ServerEndpointRegistrationServerEndpointExporter bean。警告 - 我没有检查 graphql 库的源文件,也许这个属性在别处使用。

  2. 尝试重新定义您想要/不想加载的 bean 的 BeanDefinition。这可以通过 BeanFactoryPostProcessor 来完成:

@Component
public class SampleBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
     // find the bean definition of the bean you need and try to:
     // - set "primary" on it
     String bdName = ... here put the name of the bean you want to fine-tune
     BeanDefinition beanDefinition =  beanFactory.getBeanDefinition(bdName);
     beanDefinition.setPrimary(true);
     // ... or ... 
     // - remove it altogether from the application context (bean factory)
     ((BeanDefinitionRegistry)beanFactory).removeBeanDefinition(bdName);

澄清一下 - bean 工厂后处理器在应用程序启动期间由 spring 调用,此时 bean 定义已经解析但在实际注入发生之前。

【讨论】:

  • 非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-09
  • 2016-02-22
  • 2013-11-09
  • 1970-01-01
  • 2011-05-12
  • 2018-05-15
相关资源
最近更新 更多