Situation:
Imagine we have an interface named Formatter
public interface Formatter {
public String format();
}
We have two implementations
@Component("foo")
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
@Component("bar")
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
Now, imaging we have a service that uses this Formatter
@Service
public class FormatterService {
@Autowired
private Formatter formatter;
}
Since, we have multiple implementations available, we need to explicitly specify the implementation by bean name with @Qualifier
Like this:
@Service
public class FormatterService {
@Autowired
@Qualifier("bar")
private Formatter formatter;
}
Task:
the value of the qualifier is present in application.yml
as formatter.type
, and we want to read this value and autowire it accordingly.
Problem:
@Qualifier
annotation does not support property placeholder or SpEL
Proposal: It would be better if it supports those two. Because without this, we need to
- Define a
@Configuration
class - Expose a
@Bean
method returningFormatter
which accepts a @Value("${placeholder}") - Manually creates a bean depending on the argument passed.
Like this:
@Configuration
public class FormatterSpi {
@Bean
public Formatter formatter(@Value("${formatter.type:foo}") String formatterType) {
switch(formatterType) {
case "foo":
return new FooFormatter();
case "bar":
return new BarFormatter();
default:
throw new NoSuchBeanDefinitionException("No bean of type" + formatterType + " found!");
}
}
}
Comment From: jhoeller
Qualifiers are meant to be static metadata, conceptually an extension of the type declaration. I'm afraid that dynamically resolving values for qualifier comparisons is not compatible with that model and would require significant reworking of the injection resolution algorithm (as well as the internal caching assumptions). Since qualifiers are a special-purpose solution on their own already (we recommend the use of distinct subtypes instead), we have no intentions to make them dynamic.
An @Bean
factory method along the lines of what you quoted above is the recommended solution indeed.
Comment From: Syuziko
Another solution could be by using @ConditionalOnProperty. So only one bean will be created at the smae time based on the property value.