Reputation: 935
Using spring-boot-2 I would like to create a custom autoconfiguration that defines a dynamic amount of beans of the same type.
For example I have the following ConfigurationProperties
to define an AmazonS3
client
@Setter
@Getter
@ConfigurationProperties("aws")
public class S3ClientProperties {
private Map<String, S3Config> s3 = new HashMap<>();
@Setter
@Getter
public static class S3Config {
private String accessKey;
private String secretKey;
private String bucketName;
private String region;
// ...
}
}
And if I have a yaml like this:
aws.s3:
primary:
bucketName: bucket1
region: eu-west-1
secondary:
bucketName: bucket2
region: eu-east-1
It would be great to get 2 Bean of the type AmazonS3
registered to the ApplicationContext
, one with the name primary
, one with the name secondary
.
Is there a more convenient/better way of doing so except having a @PostContstruct
on my @Configuration
, autowiring the context and adding those beans manually?
Upvotes: 3
Views: 1543
Reputation: 3499
You can use ConditionalOnProperty or multiple conditions, simple example (with your yaml
):
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
Map<String, SomeService> someServiceBeans = context.getBeansOfType(SomeService.class);
someServiceBeans.forEach((key, value) ->
System.out.println("bean name: " + key + " ,bean class: " + value.getClass().getSimpleName()));
}
// some service
public interface SomeService {
}
// service if `aws.s3.primary` property exists
@Service("primaryService")
@ConditionalOnProperty("aws.s3.primary.bucketName")
public static class SomePrimaryService implements SomeService {
}
// service if `aws.s3.secondary` property exists
@Service("secondaryService")
@ConditionalOnProperty("aws.s3.secondary.bucketName")
public static class SomeSecondaryService implements SomeService {
}
// service if `SomeService` bean missing ( `aws.s3.primary` & `aws.s3.secondary` not exists )
@Service("defaultService")
@ConditionalOnMissingBean(SomeService.class)
public static class SomeDefaultService implements SomeService {
}
// service with `custom` condition and `class` condition (multiple conditions)
@Service("customService")
@ConditionalOnCustom
@ConditionalOnClass(SomeDefaultService.class)
public static class SomeCustomService implements SomeService {
}
// annotation for custom condition
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(CustomCondition.class)
public @interface ConditionalOnCustom {
}
// any custom condition
public static class CustomCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// do something
return true;
}
}
}
application output:
bean name: customService ,bean class: SomeCustomService
bean name: primaryService ,bean class: SomePrimaryService
bean name: secondaryService ,bean class: SomeSecondaryService
Also take a look at Creating Your Own Auto-configuration
Upvotes: 1