I want to implement the folowing structure:
Main configuration class in my project:
@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()
}
}
Service bean which implements MessageListener interface
@Service
public class MyMessageListener implements MessageListener {
...
}
and module called message-consumer which also contains autoconfiguration
@Configuration
public class MessageConsumerAutoConfig {
@Bean
@ConditionOnBean(MessageListener.class)
public MessageConsumer messgageConsumer(MessageListener listener) {
// some action with listener there
}
}
This works fine untill i'm setting @ConditionOnBean(MainConfig.class)
on my MyMessageListener class
@Service
@ConditionOnBean(MainConfig.class)
public class MyMessageListener implements MessageListener {
...
}
Spring tries to call method public MessageConsumer messgageConsumer(MessageListener listener) even if there are no MessageListener.class objects in spring context found. Spring also skips initialization of the MyMessageListener bean.
NOTES: MessageListener is an interface from message-consumer module. The code above is just an example, not real project code I'm using Spring 4.3.9.RELEASE with spring-boot-starter 2.0.0.RELEASE
In my case I don't want to instantiate MyMessageListener bean if MainConfig.class bean not found in spring context and obviously MessageConsumer also shouldn't be instantiated. Someone have an ideas? How can I implement the case when a bean depeands on another bean which also depends on atother one?
if you want to via MainConfig is null to judge the condition, there is wrong, because the @ConditionalOnBean
The condition can only match the bean definitions
you can look the comment is this annotation
so the @ConditionOnBean(MainConfig.class) in your MyMessageListener always valid, this usage is wrong, if you want to controll the initialization, you could use the following processing method. we use the variable to controller whole MainConfiguration, so your mainConfig and myMessageListener is instantiated only when the @ConditionalOnProperty is match.
@Configuration
@ConditionalOnProperty(prefix = "mainconfig", name = "enable", havingValue = "true")
@Import(MessageConsumerAutoConfig.class)
public class MainConfiguration {
@Bean
public MainConfig mainConfig() {
return new MainConfig();
}
}
but there is still the wrong in your writing. when the property mainconfig.enable = false.
the @ConditionalOnBean(MainConfig.class) is match, so the final context is not exist the instance. but your @ConditionOnBean(MessageListener.class) that assing your MessageConsumerAutoConfig#messgageConsumer is also match like we talk about above, the it just check the bean definitions(not the final bean definitions), the MyMessageListener is already assign @Service, so it exists in the first phase before filter, the messgageConsumer invoke will throw exception that MessageConsumerAutoConfig require MessageListener but not found, you could write the simple processor to observe the process order
@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"));
}
}
after run it, you could observe the process in the org.springframework.boot.autoconfigure.condition.OnBeanCondition#getMatchOutcome process is before the final definitions registry.
so the correct writing, it think it is, as follow
@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 {
...
}