<dependency> <groupId>com.example</groupId> <artifactId>A</artifactId> // it provide AFactory.class <version>1</version> <option>true</option> <scope>provided</scope> </dependency> <dependency> <groupId>com.example</groupId> <artifactId>B</artifactId> // it provide BFactory.class <version>1</version> <option>true</option> <scope>provided</scope> </dependency>
In my starter project, I will write a XxxAutoConfiguration
that uses AFactory or BFactory.
I’ve tried:
@Configuration public class XxxAutoConfiguration { private AFactory aFactory; private BFactory bfactory; @Autowired(required = false) public void setaFactory(AFactory aFactory) { this.aFactory = aFactory; } @Autowired(required = false) public void setBfactory(BFactory bfactory) { this.bfactory = bfactory; } @Bean public Something something(){ if(aFactory != null){ return new Something(aFactory); }else if(bfactory != null){ return new Something(bfactory); }else{ throw new IllegalStateException(); } } }
but it doesn’t work.
I know I can write three AutoConfiguration respectively with @ConditionalOnBean(AFactory.class)
, @ConditionalOnBean(BFactory.class)
and @ConditionalOnMissingBean(....)
to solve the problem, but it’s
far from elegant… do you have any good solution? Thanks a lot.
Advertisement
Answer
Writing code that uses a class that may not exist on the classpath at runtime is not good idea. Writing the code so it doesn’t cause NoClassDefFoundError
is troublesome.
The standard Spring Boot way can e.g. be seen in the source code of DataSourceConfiguration
.
In your case, you can do this:
abstract class XxxAutoConfiguration { @Configuration @ConditionalOnBean(AFactory.class) @ConditionalOnMissingBean(Something.class) @AutoConfigureOrder(1) // Try AFactory first static class ASomething { private AFactory aFactory; @Autowired void setaFactory(AFactory aFactory) { this.aFactory = aFactory; } @Bean Something something() { return new Something(this.aFactory); } } @Configuration @ConditionalOnBean(BFactory.class) @ConditionalOnMissingBean(Something.class) @AutoConfigureOrder(2) // Try BFactory second static class BSomething { @Bean Something something(BFactory bFactory) { return new Something(bFactory); } } }
As you can see, you can do it using @Autowired
or using a parameter, either way should work.
In case both AFactory
and BFactory
are present on the classpath, you can e.g. use @AutoConfigureOrder
to specify which one wins, as shown here. There are other @ConditionalXxx
annotations for more complex ways.
The XxxAutoConfiguration
class is really just a pseudo-package for keeping it all together.