7

I want to make my spring-boot configuration class A dependent on another configuration class B, i.e. A configuration is evaluated only if B configuration is evaluated.

In the real context, I have hundreds of Ai configurations and only one B, and I want to implement a way to exclude all the Ai configs by excluding only B during tests.

I tried the following:

@Configuration
@ConditionalOnBean(type = "org.my.B")
public class A1AutoConfiguration {
// ...
}

Where B is a unconditioned configuration class.

But when I run mvn spring-boot:run -Ddebug=true I see that A is never evaluated because B is missing. While the beans created inside B are in the application context, B itself is not.

I though I can make the Ai configuration classes dependent on beans created inside B but I don't like so much this solution.

Is there a cleaner (and working) way to implement such a dependency mechanism?

Nicola Ferraro
  • 4,051
  • 5
  • 28
  • 60
  • I don't understand what's the problem here, you made all Ai dependent on B and there is no B since you are excluding B explicitly, so they are not loaded. Isn't this the expected behaviour? What are you trying to do? – Nick Vanderhoven Nov 25 '16 at 12:46
  • No, they are excluded **always**, not just when I disable `B`. – Nicola Ferraro Nov 25 '16 at 14:05

2 Answers2

7

The key is to make sure that things are ordered correctly. It does not make any sense to request A to only apply if B is present if you can't make sure that B is evaluated first.

The hundreds part frightens me a bit. If As and B are auto-configuration, you can use the following

@AutoconfigureAfter(B.class)
@ConditionalOnBean(B.class)
public class A123AutoConfiguration { ...}

If As and B are not auto-configuration, you need to make sure B is processed first so you can't rely on regular classpath scanning for those.

Stephane Nicoll
  • 31,977
  • 9
  • 97
  • 89
  • Thanks, this works in my case, but it doesn't work always. When the `A123AutoConfiguration` is placed in the main module instead of an external module with the "spring.factory" file, it is not able to find B and fails with a missing bean error. – Nicola Ferraro Nov 25 '16 at 15:38
  • If you want I can share an example to show the problem. – Nicola Ferraro Nov 25 '16 at 15:39
  • You can use a string litteral if it's not on the classpath. But that's weird as well. If I misunderstood and it's supposed to be there, yes a sample would help – Stephane Nicoll Nov 26 '16 at 08:14
  • Filed https://github.com/spring-projects/spring-boot/issues/7514 to track the problem. – Nicola Ferraro Nov 29 '16 at 08:48
  • `A123AutoConfiguration` isn't an auto-configuration. This has been raised here explicitly. – Stephane Nicoll Nov 29 '16 at 10:30
  • So, if I've understood correctly: `@AutoConfigureAfter` applies to auto-configuration classes only, while @Configuration classes placed in the same package as the main class are created by component scanning, they behave almost like auto-configuration classes but they are not auto-configuration classes (they are normal configuration classes). So the annotation has no effect on them. – Nicola Ferraro Nov 29 '16 at 12:39
  • No you didn't. What makes a `@Configuration` an auto-configuration is the presence of it in `spring.factories` and it not being picked up by component scan. `@ConditionalOnBean` on a non-auto-configuration makes no sense at all. – Stephane Nicoll Nov 29 '16 at 13:18
  • @StephaneNicoll So how to write the exposure of a regular bean on a condition over an autoconfigured one?. Are we really forced to convert our configuration into autoconfiguration? If I import an starter, do I have to use spring.factories to make my configuration dependant on it? – Whimusical Feb 16 '19 at 02:39
  • If you want to define a bean only if another bean has not been provided (`ConditionalOnMissingBean`) you have to use auto-configuration. The Javadoc of the condition tells you that already. There is nothing we can do about that, you can't expect the condition to match properly if you don't order things sensibly. – Stephane Nicoll Feb 17 '19 at 09:05
0

I would say that such group of beans is suitable for separate library or sub-module, so that they are independent. Including mechanism can be component scanning on root package of such library or sub-module.

Other option is to use Spring profiles. Mark your beans with @Profile annotation and use @ActiveProfiles to enable certain group of beans during test.

luboskrnac
  • 23,973
  • 10
  • 81
  • 92