Reputation: 411
I have a configuration class which registers beans based on a very simple condition (checking a property value in application.properties). The configuration class and the condition are the following:
@Configuration
@Conditional(DatabaseConfigurationCondition.class)
@ComponentScan(basePackageClasses = DBConfigComponents.class)
public class DatabaseConfigurationLoader {
@Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
and
public class DatabaseConfigurationCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("configuration.type").contains("db");
}
}
In addition of the beans registered in this configuration class I have component scan which scans for other components. When the condition is not met, I expect the beans which are defined in the configuration class not to be registered (which happens to be a case), but also I expect other classes which are annotated with @Component
(or @Repository
, @Service
, etc.. ) and are in same folder as DBConfigComponents.class
marker interface not to be registered, which does not happen. Beans which are scanned are always registered, no matter if the condition is fulfilled or not.
When I put the @Conditional(DatabaseConfigurationCondition.class)
on each @Component
annotated class, than it's working correctly, but I don't want to put it on each class separately.
Any suggestion?
Upvotes: 4
Views: 9095
Reputation: 244
The best way to achieve this is not to annotate these beans using @Component / @Service and @Repository annotations. Instead you should return these as part of the configuration you have setup which would be DatabaseConfigurationLoader
. See sample below.
@Configuration
@Conditional(DatabaseConfigurationCondition.class)
public class DatabaseConfigurationLoader {
@Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
@Bean
public SomeService someService() {
return new SomeService();
}
@Bean
public SomeComponent someComponent() {
return new SomeComponent();
}
}
Note: Typically
@Configuration
with@Conditional
are used in libraries that you want to include in your spring boot application. Such libraries should not share the same package as your spring boot application. Thus they should not be picked up by@ComponentScan
annotation. Beans from libraries should not be annotated with@Component
/@Service
/@Repository
annotations. Spring suggests using AutoConfiguration for that. See https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html & https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
Upvotes: 2
Reputation: 411
Fortunately, I managed to fix this. The problem in my case was that I had another @ComponentScan
annotation placed in other configuration class in other Maven module - not conditional on any property. The components which are in same package as DBConfigComponents
marker interface were actually scanned by the other configuration class.
The way @ComponentScan
works is on package level. Although, in different Maven modules, both configuration classes were in same package. @ComponentScan
works perfectly fine with @Conditional
. No need @Conditional
to be placed on each component separately.
Upvotes: 2
Reputation: 1219
No need to implement Condition interface, you need to use '@ConditionalOnProperty' annotation:
@Configuration
@ComponentScan(basePackageClasses = DBConfigComponents.class)
@ConditionalOnProperty(name = "configuration.type", havingValue = "db")
public class DatabaseConfigurationLoader {
@Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
you can use 'prefix' instead of 'havingValue' depending on your needs.
Upvotes: 0