Reputation: 899
In Spring, when I inject a list of beans, I only want to inject specific implementations of the interface, when used from different places. Is this possible to do? What would be the cleanest way to configure this? For example, I have the following validators:
public interface Validator {
Optional<Error> validate(MultipartFile file);
}
public class Validator1 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
public class Validator2 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
public class Validator3 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
And then I have a validation service which looks similar to this:
public class ValidationService {
@Autowired
private List<Validator> validators;
public List<Error> validate(MultipartFile file) {
List<Error> errors = new ArrayList<>();
validators.forEach(v -> {
Optional<Error> error = v.validate(file);
if (error.isPresent()) {
errors.add(error.get());
}
});
return errors;
}
}
And then I have some services, which use the ValidationService, e.g:
public class Service1 {
@Autowired
private ValidationService validationService;
public void doStuff(MultipartFile file) {
...
validationService.validate(file);
...
}
}
public class Service2 {
@Autowired
private ValidationService validationService;
public void doStuff(MultipartFile file) {
...
validationService.validate(file);
...
}
}
When Service1 calls validate, I only want Validator1 and Validator2 to have been injected into the ValidatorService. When Service2 calls validate, I only want Validator2 and Validator3 to have been injected into the ValidatorService.
Hope I have explained this clearly enough. Thanks in advance for any help offered.
Upvotes: 1
Views: 4627
Reputation: 731
I am not sure that it is possible to have overlap (use the same bean in both lists) with this schema.
@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Set1 {}
@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Set2 {}
Validator1
with Set1
and Validator2
and Validator3
with Set2
@Set1
@Component
public class Validator1 implements Validator {}
@Set2
@Component
public class Validator1 implements Validator {}
@Set2
@Component
public class Validator1 implements Validator {}
@Set1
@Autowired
private List<Validator> validators;
@Set2
@Autowired
private List<Validator> validators;
Upvotes: 0
Reputation: 38300
This is likely not the best way to do this. Here is how I would do it, based on my current understanding of Spring.
Summary:
List<Validator>
that contains Validator1 and Validator2 and create a second List<Validator>
that contains Validator2 and Validator3.The code should be something like this:
@Configuration
public class ValidatorLists
{
private void getAndAddBean(
final ApplicationContext applicationContext,
final List<Validator> list,
final String beanName)
{
final Validator bean;
bean = applicationContext.getBean(beanName);
if (bean != null)
{
list.add(bean);
}
}
@Bean("ValidatorList1")
public List<Validator> validatorList1(final ApplicationContext applicationContext)
{
Validator bean;
final List<Validator> returnValue = new LinkedList<>();
getAndAddBean(applicationContext, returnValue, "ValidatorImpl1");
getAndAddBean(applicationContext, returnValue, "ValidatorImpl2");
return returnValue;
}
@Bean("ValidatorList2")
public List<Validator> validatorList2(final ApplicationContext applicationContext)
{
Validator bean;
final List<Validator> returnValue = new LinkedList<>();
getAndAddBean(applicationContext, returnValue, "ValidatorImpl2");
getAndAddBean(applicationContext, returnValue, "ValidatorImpl3");
return returnValue;
}
}
Then reference the list by qualifier.
@Autowired
@Qualifier("ValidatorList1")
private List<Validator> validators;
Upvotes: 0
Reputation: 976
Create the bean like this with @Qualifier annotations --
@Qualifier("validator1")
public class Validator1 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
@Qualifier("validator2")
public class Validator2 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
@Qualifier("validator3")
public class Validator3 implements Validator {
public Optional<Error> validate(MultipartFile file) {
// do stuff
}
}
and inject it like this ---
@Autowired("validator1")
private ValidationService validationService;
Update
You can also create a bean collection for all the validators like this -
@Bean("validators")
public List<Validator> validatorList(Validator1 validator1, Validator2 validator2, Validator3 validator3) {
return Arrays.asList(validator1, validator2, validator3);
}
and the inject the list bean as --
@Autowired("validators")
private List<Validator> validators;
Check this page fore a detailed example - https://www.baeldung.com/spring-injecting-collections
Upvotes: 1