【问题标题】:How to replace an existing bean in spring boot application?如何在 Spring Boot 应用程序中替换现有的 bean?
【发布时间】:2017-01-17 10:42:29
【问题描述】:

我的 Spring Boot 应用程序,已经在一个自动配置类中从一个依赖 jar 中创建了一个 bean,如下所示:

@Bean
@Order(100)
public StaticRouteLocator staticRouteLocator(AdminServerProperties admin) {
    Collection<ZuulRoute> routes = Collections
            .singleton(new ZuulRoute(admin.getContextPath() + "/api/turbine/stream/**",
                    properties.getUrl().toString()));
    return new StaticRouteLocator(routes, server.getServletPrefix(), zuulProperties);
}

现在我想替换这个 bean,但我仍然需要这个 jar,它有这个不需要的 Bean 创建。所以我在我的主要自动配置类中添加了另一个 bean 创建方法,如下所示:

  @Bean(name="patchedStaticRouteLocator")
  @Order(10)
  @Primary
  @ConditionalOnMissingBean
  public StaticRouteLocator patchedStaticRouteLocator(AdminServerProperties admin) {
    Collection<ZuulProperties.ZuulRoute> routes = Collections
        .singleton(new ZuulProperties.ZuulRoute(admin.getContextPath(),
            properties.getUrl().toString()));
    return new StaticRouteLocator(routes, server.getServletPrefix(), zuulProperties);
  }

但这未能替换目标 bean。错误信息清晰易懂:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.cloud.netflix.zuul.filters.RouteLocator] is defined: more than one 'primary' bean found among candidates: [routeLocator, patchedStaticRouteLocator, staticRouteLocator, compositeRouteLocator, applicationRouteLocator]

我的问题是在 Spring Boot 中替换此类现有 bean 的正确方法是什么?提前致谢。

【问题讨论】:

    标签: spring-boot javabeans spring-java-config


    【解决方案1】:

    在这种情况下的问题是,您没有用名称 staticRouteLocator 替换此 bean。您正在创建另一个名为 patchedStaticRouteLocator 的 bean。这通常不是问题,但似乎不是您想要的。

    NoUniqueBeanDefinitionException 被提出是因为您还添加了 @Primary 注释,现在至少有两个 bean 被标记为主要接线候选。 Spring 不知道现在应该怎么做。

    如果你真的想覆盖第一个 bean,给它同样的名字。默认名称(如果没有明确指定其他名称)将是您在 @Configuration 类中定义的方法的名称。在您的情况下,这将是 patchedStaticRouteLocator。 (目前您还使用 @Bean 注释 name 属性再次定义了相同的名称,这有点多余且不需要。)

    如果你想用名称/别名 staticRouteLocator 替换 bean,给你的新 bean 赋予相同的名称,所以定义如下:

    @Bean(name="staticRouteLocator")

    这应该覆盖第一个 bean。

    你可以用这样的支票来计算你的豆子:

    import static org.hamcrest.CoreMatchers.is;
    import static org.junit.Assert.assertThat;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.context.ApplicationContext;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class BeanConfigTest {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Test
        public void countBeanNames() {
            final String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            assertThat(beanDefinitionNames.length, is(1));
        }
    
    }
    

    只需将 1 替换为您期望的计数(之前和之后)。

    【讨论】:

    • 感谢上述说明,尽管此解决方案没有按预期工作,但这些说明消除了我的一些困惑。但是我通过实现一个BeanPostProcessor 组件来完成它。
    • 不客气。不过很高兴你找到了一个可行的解决方案。
    • @leo 请添加您如何解决问题的答案并将其标记为已接受。你可以接受你自己的anwser。它会帮助别人
    【解决方案2】:

    通过实现BeanPostProcessor 实现的目标:

    @Component
    @Slf4j
    public class StaticRouteLocatorPostBeanProcessor implements BeanPostProcessor {
    
      @Autowired
      private TurbineProperties properties;
    
      @Autowired
      private ServerProperties server;
    
      @Autowired
      private ZuulProperties zuulProperties;
    
      @Autowired
      private AdminServerProperties adminServerProperties;
    
      @Override
      public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
      }
    
      @Override
      public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (StaticRouteLocator.class.isAssignableFrom(bean.getClass())) {
          Collection<ZuulProperties.ZuulRoute> routes = Collections
              .singleton(new ZuulProperties.ZuulRoute(adminServerProperties.getContextPath(),
                  properties.getUrl().toString()));
          log.info("Began to replace the StaticRouteLocator bean.");
          return new StaticRouteLocator(routes, server.getServletPrefix(), zuulProperties);
        }
        return bean;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-26
      • 1970-01-01
      • 2022-01-13
      • 1970-01-01
      • 1970-01-01
      • 2019-09-07
      相关资源
      最近更新 更多