11

I have a list of gaming rooms which is created by Spring. Each rooms corresponds some rules (which is enum), and the code is:

@Bean
List<Room> rooms() {
    return Arrays.stream(Rules.values())
        .map(rule -> new Room(rule))
        .collect(Collectors.toList());
}

But now I need the rooms to be @Beans too: I want Spring to process @EventListener annotation in them. However, I don't want to declare them manually in config as the Rules enum can be updated in the future. How can I solve this problem? Thanks.

awfun
  • 2,316
  • 4
  • 31
  • 52

2 Answers2

6

It can be accomplished by invoking another method which is marked with @Bean i.e. creates Room bean as shown below

@Bean
List<Room> rooms() {
    return Arrays.stream(Rules.values())
        .map(rule -> room(rule))
        .collect(Collectors.toList());
}

@Bean
Room room(Rule rule) {
    return new Room(rule);
}

This should suffice without need of @EventListener.

Let know in comments if any more information is required.

Bond - Java Bond
  • 3,972
  • 6
  • 36
  • 59
  • If the `room()` bean's scope is not specified, all the rooms in list have the same rule: `Rules.values()[0]`. So I have to mark the `room()` bean as `@Scope("prototype")`, right?. But after I send the event it has to proceed, I get exception: `Error creating bean with name 'room' defined in ApplicationConfig: Unsatisfied dependency expressed through method 'room' parameter 0; nested exception is NoSuchBeanDefinitionException: No qualifying bean of type 'Rules'`. Also my IDE argues that for `room()` bean there are no candidates as a parameter. – awfun Feb 20 '17 at 11:15
  • 1
    Yes, the bean scope should be `prototype`. Also regarding the error are the method `public` ? (*not sure though about this*) – Bond - Java Bond Feb 20 '17 at 11:42
  • Methods are package-private, but that doesn't matter - if it did, I would get the exception at startup – awfun Feb 20 '17 at 14:11
  • okies.. could you try with `public` method access and also re-compile with arguments names preserved – Bond - Java Bond Feb 20 '17 at 14:31
  • 1
    @Bond-JavaBond, Thanks for the answer, but the fact is it will try to create each Room with same spring-bean name as "room" as per the second method name, hence only the first Room will be created. At least this is something happenning with me. Any idea on that? – Abhishek Chatterjee Jul 24 '20 at 10:59
3

In my opinion, there is another option

@Bean
List<Room> rooms() {
  return Arrays.stream(Rules.values())
    .map(rule -> {
        Room r = room(rule);
        this.beanFactory.initializeBean(r, <some unique name>);
        this.beanFactory.autowireBean(r);
        this.beanFactory.registerSingleton(<same name>, r);
    })
    .collect(Collectors.toList());
}

private Room room(Rule rule) {
  return new Room(rule);
}

within a @Configuration class having,

@Autowired
private ConfigurableListableBeanFactory beanFactory;
Abhishek Chatterjee
  • 1,962
  • 2
  • 23
  • 31