如何在spring boot应用程序中替换现有的bean?

leo*_*leo 3 javabeans spring-boot spring-java-config

我是一个Spring引导应用程序,在一个自动配置类中已经创建了一个bean,它来自一个依赖的jar,如bellow:

@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);
}
Run Code Online (Sandbox Code Playgroud)

现在我想替换这个bean,但我仍然需要这个有不必要的Bean创建的jar.所以我在我的主要自动配置类中添加了另一个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);
  }
Run Code Online (Sandbox Code Playgroud)

但是这无法替换目标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]
Run Code Online (Sandbox Code Playgroud)

我的问题是什么是在春季靴子中更换这种现有豆子的正确方法?提前致谢.

leo*_*leo 6

通过实施实现的目标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;
  }
}
Run Code Online (Sandbox Code Playgroud)


Kev*_*ers 5

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

NoUniqueBeanDefinitionException之所以引发,是因为您还添加了@Primary注释,现在至少有两个bean被标记为主要布线候选者.Spring不知道它现在该做什么.

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

如果要使用name/alias staticRouteLocator替换bean,请为新bean指定相同的名称,因此将其定义为:

@Bean(name="staticRouteLocator")

这应该覆盖第一个bean.

您可以使用以下支票计算您的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));
    }

}
Run Code Online (Sandbox Code Playgroud)

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