0

Say I have:

interface A { ... }
class A1 implements A { ... }
class A2 implements A { ... }

@Lazy
@Configuration
class SpringConfig {
  @Bean
  A a1(DepA11 depA11, DepA12 depA12, ...) {
  }

  @Bean
  A a2(DepA21 depA21, DepA22 depA22, ...) {
  }
}

Now let's say there's some complex logic that depends on some injected dependencies that returns an int:

int choose(DepChoose1 depChoose1, DepChoose2 depChoose2, ...) {
  // Complex logic that depends on depChoose1, depChoose2, ...
  int res = ...; 
  return res;
}

and that I want Spring to autowire either a1 or a2 based on the return value.

It is imperative not to instantiate the other bean (or beans in general - we can have a3, a4, ...), as each of them incur heavy processing on startup and also can have side-effects that have to be avoided if the other bean is to be chosen initially.

A1 and A2 are stream-like sources of items for other parts of the system to consume. Some of their (not immediate) dependencies have initialization via @PostConstruct.

Their dependencies are also push-based. Whatever they fetch, they push to B which then forwards to other consumers. So just initializing them would create unwanted pushes.

I thought about using @Conditional, but it inherently doesn't support dependencies.

Is there a succinct way to do this in Spring?

levant pied
  • 3,886
  • 5
  • 37
  • 56

2 Answers2

1

Perhaps not the solution you're looking for, but if instantiating the (unused) beans is too costly, instantiate factories instead.

Jiri Tousek
  • 12,211
  • 5
  • 29
  • 43
  • How do I get around instantiating dependencies though? E.g. `A1Factory` would need `DepA11`, `DepA12`, ... in order to create `A1`. I would then need to create factories for these as well and their dependencies as well and... why do I need Spring then :) – levant pied Mar 07 '19 at 23:11
  • I'm afraid we might be dealing with an [XY problem](https://en.wikipedia.org/wiki/XY_problem) here. The question is not very specific as to what are you trying to achieve, and with what constraints. In general, what you'll run into is that Spring performs the initial configuration processing and instantiation at some point in time, and if your instantiation logic depends on other beans, that's too early for you. Lazy-init beans don't seem to match your problem. – Jiri Tousek Mar 07 '19 at 23:19
  • I've dealt with similar issues in the past by creating two Spring contexts, the second one parameterized based on some computations in the first one (by means of XML configuration with property placeholders - they can be used even for bean refs / bean class names!), but it's hard to tell whether that would be overkill in your situation, or whether it could even be used. – Jiri Tousek Mar 07 '19 at 23:20
  • Thanks Jiri - I edited the answer to add a few more bits, but I'm not sure how to be more specific and not lose the generality of the solution that I'm after. – levant pied Mar 07 '19 at 23:57
1

Two possibilities:

  • if the choose always returns the same value, in other words, you always want to have either a1 or a2 in your context then just define one @Bean method, perform the checks in it and return a1 or a2 from it
  • if the choose returns different values, then create a new bean which will act as a factory, so instead of injecting a1 or a2 you will inject the factory and then call e.g. getBean() on it, which will then call choose and return a1 or a2
Adam Siemion
  • 15,569
  • 7
  • 58
  • 92
  • "choose always returns the same value" Yeah that what I want, but if I do that then all the dependencies will be instantiated, which is where the cost is among other things. How do I get around that? – levant pied Mar 07 '19 at 23:10
  • @levantpied the `@Bean` method will only instantiate the required dependencies – Adam Siemion Mar 07 '19 at 23:18
  • From the OP's description it looks like the result of `choose()` depends on some other beans being already initialized - it might be a tight squeeze to perform the checks before `a1` or `a2` is instantiated, but already with `depChoose1` and `depChoose2` in a consistent state. – Jiri Tousek Mar 07 '19 at 23:23
  • @JiriTousek That's correct. I expect `depChoose1` and `depChoose2` to be fully instantiated, so I can use their methods to figure out whether to instantiate `A1` or `A2`, but I want to avoid instantiating `A2` and any of it's `DepA2x` dependencies if I chose `A1`. – levant pied Mar 07 '19 at 23:33