I have a Qualifier where I read from
public class TestController{
@Autowired
@Qualifier("jdbc")
private JdbcTemplate jtm;
//.....
}
The qualifier "jdbc" is the bean defined as
@Bean(name = "jdbc")
@Autowired
public JdbcTemplate masterJdbcTemplate(@Qualifier("prod") DataSource prod) {
return new JdbcTemplate(prod);
}
This is the which returns the datasource for that qualifier and works fine.
Now I want to make the Qualifier name to be read from the application.properties. So I changed my code to
public class TestController{
@Autowired
@Qualifier("${database.connector.name}")
private JdbcTemplate jtm;
//.....
}
where database.connector.name=jdbc
in my application.properties.
But when i do this this throws an error of
APPLICATION FAILED TO START
Description:
Field userService in main.java.rest.TestController required a bean of type 'org.springframework.jdbc.core.JdbcTemplate' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.jdbc.core.JdbcTemplate' in your configuration.
Any help is appreciated.
Using the @Value Annotation The @Value annotation in spring boot reads the value from the application properties file and assigns it to a java variable. To read the property, the property key name must be provided in the @Value annotation.
By using the @Qualifier annotation, we can eliminate the issue of which bean needs to be injected. By including the @Qualifier annotation, together with the name of the specific implementation we want to use, in this example Foo, we can avoid ambiguity when Spring finds multiple beans of the same type.
We can use @Qualifier and @Primary for the same bean. Use @Qualifier to inject specific bean otherwise Spring injects bean by default which is annotated with @Primary.
What is @Qualifier in Spring Boot? @Qualifier is an annotation used to specify which Spring managed bean should be injected via @Autowired. If you were to run the example without @Qualifier, the following exception would be thrown... This error says it all.
As @Hemant already mentioned default QualifierCandidateResolver
does not resolve properties.
But you can make one, which does:
import java.lang.annotation.Annotation;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public static class AutowireCandidateResolverConfigurer implements BeanFactoryPostProcessor {
private static class EnvironmentAwareQualifierAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
private static class ResolvedQualifier implements Qualifier {
private final String value;
ResolvedQualifier(String value) { this.value = value; }
@Override
public String value() { return this.value; }
@Override
public Class<? extends Annotation> annotationType() { return Qualifier.class; }
}
@Override
protected boolean checkQualifier(BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
if (annotation instanceof Qualifier) {
Qualifier qualifier = (Qualifier) annotation;
if (qualifier.value().startsWith("${") && qualifier.value().endsWith("}")) {
DefaultListableBeanFactory bf = (DefaultListableBeanFactory) this.getBeanFactory();
ResolvedQualifier resolvedQualifier = new ResolvedQualifier(bf.resolveEmbeddedValue(qualifier.value()));
return super.checkQualifier(bdHolder, resolvedQualifier, typeConverter);
}
}
return super.checkQualifier(bdHolder, annotation, typeConverter);
}
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
DefaultListableBeanFactory bf = (DefaultListableBeanFactory) beanFactory;
bf.setAutowireCandidateResolver(new EnvironmentAwareQualifierAnnotationAutowireCandidateResolver());
}
}
With that you will be able to use @Qualifier
in a way you've asked @Qualifier("${database.connector.name}")
Full example:
@SpringBootApplication
public class SO50208018Application {
public static void main(String[] args) { SpringApplication.run(SO50208018Application.class, args); }
interface MyBean { }
static class MyBeanImpl1 implements MyBean { }
static class MyBeanImpl2 implements MyBean { }
@Bean @Qualifier("impl1")
MyBean bean1() { return new MyBeanImpl1(); }
@Bean @Qualifier("impl2")
MyBean bean2() { return new MyBeanImpl2(); }
@Component
public static class AutowireCandidateResolverConfigurer implements BeanFactoryPostProcessor {
// configurer from above
}
@Bean
CommandLineRunner run(@Qualifier("${spring.application.bean}") MyBean bean) {
return (args) -> System.out.println(bean.getClass().getName());
}
}
Run with spring.application.bean=impl1
:
com.stackoverflow.java.SO50208018Application$MyBeanImpl1
Run with spring.application.bean=impl2
:
com.stackoverflow.java.SO50208018Application$MyBeanImpl2
Qualifier doesn't resolve placeholder. You can write your TestController
class as
public class TestController {
@Value("${database.connector.name}")
private String name;
private JdbcTemplate jtm;
@Autowired
public void setJdbcTemplate(ApplicationContext context) {
jtm = (JdbcTemplate) context.getBean(name);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With