2

I have following @Configuration class, in which I am declaring a @Bean that depends on an @Autowired list of beans. However, this list is not complete when I am accessing to it. All @Bean definitions been executed, except the one defined in the same class.

@Configuration
public class MyConfig {

  @Autowired
  List<RequiredBean> requiredBeans;

  @Bean(name="ProblemHere")
  public CustomObject customObject() {
    log.info(requiredBeans.size()); // 1 (debugging, I can see is the one defined in "AnotherConfigClass")
  }

  @Bean(name="reqBeanInsideClass")
  public RequiredBean reqBean() {
    // this method does not get executed
    return new RequiredBean();
  }

}

Having other classes like;

@Configuration
public class AnotherConfigClass {

  @Bean(name="ThisOneGetsExecuted")
  public RequiredBean reqBean() {
    // this gets executed, and therefore, added to the list
    return new RequiredBean();
  }

}

Probably, the easiest solution would be to add @DependsOn("reqBeanInsideClass").

However:

  1. I wonder why it works for all @Beans defined in different classes, but not in this one.
  2. I'm not really sure that's exactly like that, and I'm afraid later on, another @Bean does not get executed
  3. I guess the correct approach should be something like

    @DependsOn(List<RequiredBean>) // Obviously this does not work
    

How should I solve this?

Update

I have copied the exact same class twice, in order to see what would happen, so now I have also:

@Configuration
public class MyConfig2 {

  @Autowired
  List<RequiredBean> requiredBeans;

  @Bean(name="ProblemHere2")
  public CustomObject customObject() {
    log.info(requiredBeans.size());
  }

  @Bean(name="reqBeanInsideClass2")
  public RequiredBean reqBean() {
    // this method does not get executed
    return new RequiredBean();
  }

}

Amazingly, by doing this, both @Beans methods (ProblemHere & ProblemHere2) are called before both reqBeanInsideClass and reqBeanInsideClass2 methods.

For some reason I guess, Springboot is able to recognize @Beans required for a class as long as they are defined in another class.

  1. Does this sound logic to anyone?
Mayday
  • 4,680
  • 5
  • 24
  • 58
  • ‍♂️ you are declaring beans but no where adding to arraylist, even how does that list have size 1? – Ryuzaki L Nov 16 '18 at 09:51
  • @Deadpool Probably because randomly other Beans get initialized first? I'm not sure, thats why I am asking which is the correct way to make this dependency – Mayday Nov 16 '18 at 09:53

1 Answers1

1

Can you not utilize the array input for @DependsOn rather than passing singular value, since it accepts String[]? That would wait for all the beans that are explicitly declared in the array before initializing, though has to be defined manually.

@Configuration
public class MyConfig {

  @Autowired
  List<RequiredBean> requiredBeans;

  @Bean(name="customObject")
  @DependsOn({"reqBeanInsideClass", "thisOneGetsExecuted"})
  public CustomObject customObject() {
    log.info(requiredBeans.size());
  }

  @Bean(name="reqBeanInsideClass")
  public RequiredBean reqBean() {
    return new RequiredBean();
  }
}

@Autowired list of beans will be same as a single bean of same type, it will contain all beans with that type or with that superclass via springs injection, the problem is the ordering of bean initialization is not controlled properly, @DependsOn with array bean input should resolve this!

Or

You can make CustomObject bean @Lazy, so it will be initialized only when it is used within the code after initialization is done. The bean must not be used within another non-lazy bean I think. Just call some logic where an @Autowired CustomObject is used, it should instantiate the bean at that moment, where the list will contain all possible RequiredBeans

@Lazy
@Bean(name="customObject")
public CustomObject customObject() {
  log.info(requiredBeans.size());
}
buræquete
  • 14,226
  • 4
  • 44
  • 89
  • I could do that, but I would love to automatically just declare new @Beans of type RequiredBean in different parts of application, and not having to get worried about specifying them 1 by 1 every time I add a new one – Mayday Nov 16 '18 at 10:33
  • no, still not working. Probably because it is being required early on. (I do not really have control on when "CustomObject" gets executed, since it is related with JPA, to create datasources etc. Actually, "CustomObject" is just to make the sample easier to understand, but in my project is "LocalContainerEntityManagerFactoryBean") However, if I take the inside class Bean into another class, it always seems to work. I just can not understand why :/ – Mayday Nov 16 '18 at 10:49
  • Oh, if `CustomObject` is a factory, then it has to be processed during the initialization, you have to utilize `@DependsOn` with explicit beans, if possible you can try to put them all in a single class and put the `CustomObject` at the bottom, the order is why your `RequiredBean` is not picked up I think. Or how about putting `@Order` on your `@Configuration` classes? Put a very high number on the one with `CustomObject` bean, and any new one you add, use `1, 2, 3, ...`, if you have access to those configuration source code of course, not sure if `@Order` would work on `@Configuration` though – buræquete Nov 16 '18 at 10:58
  • If I move the `reqBeanInsideClass` and write it into anther config class, then it works :) – Mayday Nov 16 '18 at 11:57
  • @Mayday the problem is the order of bean initialization, so in another config it is initialized before `CustomObject`, but this order might change without proper control, so risky to just accept this uncontrolled order really. Did you try my suggestions in my previous wall of text? lol – buræquete Nov 16 '18 at 11:58
  • I did not have time to try it yet, but I have it in mind, and hopefully, should work in my opinion also! Didn't answer yet, but when I have time to try it, I will answer you :) – Mayday Nov 16 '18 at 12:01
  • Sorry to dissapoint you, but seems like `@Order` in `@Configuration` does not provide any ordering on `@Bean` methods declared inside those classes :D. I need to keep researching – Mayday Nov 16 '18 at 12:15
  • @Mayday how about this madness -> `I think you can try to write a timer in your bean and execute your task once after a period that is long enough for other beans to start` – buræquete Nov 16 '18 at 12:17
  • Do you think i could wrap that list into a custom bean? Then i only need to autowire the wrapper component? It might solve the ordering issue – Mayday Nov 16 '18 at 13:10
  • @Mayday but then you'd have to manually add the beans, same as hardcoding with `@DependsOn()` lol – buræquete Nov 16 '18 at 13:11
  • @Mayday it is always proper to either pass beans in method param as dependency, or control with `@DependsOn` etc, what you are doing is just very messy, I'd recommend to hard code within depends on annotation, and just add more bean names as you add, otherwise it is really messy to control I think – buræquete Nov 16 '18 at 14:11
  • 1
    Yes, I might need to go for that solution. It just sounded to me logic that springboot is able to inject properly a list of dependencies by having all the components scan defined. But maybe in future versions heh. Thank you very much for your time :) – Mayday Nov 16 '18 at 14:13