Spring MVC 如何以正确的方式实现@ConditionalOnBean注解依赖bean链?

kgqe7b3p  于 2023-04-12  发布在  Spring
关注(0)|答案(1)|浏览(152)

我想实现以下结构:
我的项目中的主要配置类:

@Configuration
@Import(MessageConsumerAutoConfig.class)
public class MainConfiguration {

   @Bean
   public MainConfig mainConfig() {
      if (some condition) {
         log.warning("Bean shouldn't be initialized");
         return null;
      }
      return new MainConfig()
   }

}

实现MessageListener接口的服务Bean

@Service
public class MyMessageListener implements MessageListener  {
    ...
}

和名为 message-consumer 的模块,其中还包含自动配置

@Configuration 
public class MessageConsumerAutoConfig {

    @Bean
    @ConditionOnBean(MessageListener.class)
    public MessageConsumer messgageConsumer(MessageListener listener) {
        // some action with listener there
    }
}

在我在MyMessageListener类上设置@ConditionOnBean(MainConfig.class)之前,它一直工作正常

@Service
@ConditionOnBean(MainConfig.class)
public class MyMessageListener implements MessageListener  {
    ...
}

Spring会尝试调用方法public MessageConsumer messgageConsumer(MessageListener listener),即使在spring context中没有找到MessageListener.class对象。Spring还会跳过MyMessageListenerbean的初始化。
注:MessageListener是来自message-consumer模块的接口。上面的代码只是一个示例,不是真实的的项目代码,我使用的是Spring 4.3.9.RELEASEspring-boot-starter 2.0.0.RELEASE
在我的例子中,我不想示例化MyMessageListenerbean,如果在Spring上下文中找不到MainConfig.classbean,显然MessageConsumer也不应该被示例化。有人有什么想法吗?当一个bean依赖于另一个bean,而另一个bean也依赖于另一个bean时,我该如何实现这种情况?

qoefvg9y

qoefvg9y1#

如果你想通过MainConfig为null来判断条件,那就有错了,因为@ConditionalOnBean The condition can only match the bean definitions你可以看注解就是这个注解
所以你的MyMessageListener中的@ConditionOnBean(MainConfig.class)总是有效的,这种用法是错误的,如果你想控制初始化,你可以使用下面的处理方法。我们使用变量来控制整个MainConfiguration,所以只有当@ConditionalOnProperty匹配时,你的mainConfig和myMessageListener才会被示例化。

@Configuration
@ConditionalOnProperty(prefix = "mainconfig", name = "enable", havingValue = "true")
@Import(MessageConsumerAutoConfig.class)
public class MainConfiguration {

    @Bean
    public MainConfig mainConfig() {
        return new MainConfig();
    }

}

但是在你的写作中仍然存在错误。当属性mainconfig.enable = false时。
@ConditionalOnBean(MainConfig.class)是匹配的,所以最终的上下文不存在于示例中。但是您的@ConditionOnBean(MessageListener.class),assing你的MessageConsumerAutoConfig#messgageConsumer也是匹配的,就像我们上面讨论的,它只检查bean定义(不是最终的bean定义),MyMessageListener已经分配了@Service,因此它存在于filter之前的第一个阶段,messgageConsumer调用将抛出异常,即MessageConsumerAutoConfig需要MessageListener但未找到,您可以编写简单处理器来观察处理顺序

@Component
public class LookProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Iterator<String> beanNames = beanFactory.getBeanNamesIterator();
        final HashSet<String> strings = new HashSet<>();
        beanNames.forEachRemaining(beanName->{
            strings.add(beanName);
        });

        System.out.println(strings.contains("mainConfiguration"));
        System.out.println(strings.contains("messageConsumerAutoConfig"));
        System.out.println(strings.contains("myMessageListener"));
        System.out.println(strings.contains("messgageConsumer"));
    }
}

运行后,可以观察到org.springframework. Boot .autoconfigure.condition.OnBeanCondition#getMatchOutcome进程在最终定义注册表之前。
所以正确的写法,它认为是,如下

@Configuration
@ConditionalOnProperty(prefix = "mainconfig", name = "enable", havingValue = "true")
@Import(MessageConsumerAutoConfig.class)
public class MainConfiguration {

    @Bean
    public MainConfig mainConfig() {
        return new MainConfig();
    }

    @Bean
    public MyMessageListener myMessageListener() {
        return new MyMessageListener();
    }
}

public class MyMessageListener implements MessageListener  {
    ...
}

相关问题