sjngm
sjngm

Reputation: 12871

Creating Unknown Number of Beans With Configuration From Properties-Files

My situation is that I have a properties-file to configure an unknown number of beans:

rssfeed.source[0]=http://feed.com/rss-news.xml
rssfeed.title[0]=Sample feed #1
rssfeed.source[1]=http://feed.com/rss-news2.xml
rssfeed.title[1]=Sample feed #2
:

I have a configuration class to read those properties:

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "rssfeed", locations = "classpath:/config/rssfeed.properties")
public class RssConfig {

  private List<String> source = new ArrayList<String>();
  private List<String> title = new ArrayList<String>();

  public List<String> getSource() {
    return source;
  }
  public List<String> getTitle() {
    return title;
  }

  @PostConstruct
  public void postConstruct() {

  }
}

This is working nicely. However, now I want to create beans based on that. What I've tried so far is

  1. add @Bean-methods and call them from postConstruct()

      @Bean
      @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
      public SourcePollingChannelAdapter createFeedChannelAdapter(int id, String url) {
        SourcePollingChannelAdapter channelAdapter = new SourcePollingChannelAdapter();
        channelAdapter.setApplicationContext(applicationContext);
        channelAdapter.setBeanName("feedChannelAdapter" + id);
        channelAdapter.setSource(createMessageSource(id, url));
        return channelAdapter;
      }
    
      @Bean
      @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
      public FeedEntryMessageSource createMessageSource(int id, String url) {
        try {
          FeedEntryMessageSource messageSource = new FeedEntryMessageSource(new URL(url), "");
          messageSource.setApplicationContext(applicationContext);
          messageSource.setBeanName("feedChannelAdapter" + id + ".source");
          return messageSource;
        } catch (Throwable e) {
          Utility.throwAsUncheckedException(e);
          return null;
        }
      }
    
      @Bean
      @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
      public QueueChannel createFeedChannel(int id, String url) {
        QueueChannel channel = new QueueChannel();
        channel.setApplicationContext(applicationContext);
        channel.setBeanName("feedChannel" + id);
        return channel;
      }
    
      @PostConstruct
      public void postConstruct() {
        for (int x = 0; x < source.size(); x++) {
          createFeedChannelAdapter(x, source.get(x));
        }
      }
    

    However, Spring tries to autowire the parameters to those methods rather than using the parameters I provided in postConstruct().

  2. a BeanFactoryPostProcessor or a BeanDefinitionRegistryPostProcessor. However, here I don't have access to the properties-file or the RssConfig-bean from above as it's called too early in the lifecycle.

What do I need to do generate those dynamic number of beans? I'm probably just a tiny little step away... I prefer a Java configuration-solution over an XML-solution.

Upvotes: 3

Views: 1933

Answers (1)

Dave Syer
Dave Syer

Reputation: 58124

You need to register the bean definitions (not call @Bean methods), so BeanDefinitionRegistryPostProcessor or ImportBeanDefinitionRegistrar are the best ways to do that currently. You can grab the properties file and bind to it using PropertiesConfigurationFactory (in Spring Boot) instead of using @ConfigurationProperties, or maybe you could use a parent context or a standalone SpringApplication to create and bind your RssConfig inside your bean definition registration code.

Upvotes: 4

Related Questions