Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read Qualifier from property file in spring boot?

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.

like image 677
Ricky Avatar asked May 07 '18 06:05

Ricky


People also ask

How do you read a key value pair from properties file in Spring boot?

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.

What does @qualifier do in Spring boot?

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.

Can we use @qualifier and @primary together?

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 example?

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.


2 Answers

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
like image 173
Bohdan Levchenko Avatar answered Oct 26 '22 23:10

Bohdan Levchenko


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);
    }
}
like image 41
Pratapi Hemant Patel Avatar answered Oct 26 '22 23:10

Pratapi Hemant Patel