Search code examples
javaspringresthighlevelclient

How does spring framework knows how to instantiate RestHighLevelClient in this example?


I'm following this post which explains how to use Java High Level REST Client (JHLRC) to connect with ElasticSearch.

The important parts for this questions are in ElasticsearchConfig.java:

@Configuration
public class ElasticsearchConfig {

    ...

    @Bean(destroyMethod = "close")
    public RestHighLevelClient restClient() {

        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials(userName, password));

        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port))
                .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));

        RestHighLevelClient client = new RestHighLevelClient(builder);
        return client;

    }


}

and ProfileService.java:

@Service
public class ProfileService {

    private RestHighLevelClient client;
    private ObjectMapper objectMapper;

    @Autowired
    public ProfileService(RestHighLevelClient client, ObjectMapper objectMapper) {
        this.client = client;
        this.objectMapper = objectMapper;
    }

    ...

We are autowiring RestHighLevelClient and ObjectMapper, so how does Spring knows that the RestHighLevelClient instance we need comes from ElasticsearchConfig.restClient()?


Solution

  • Spring does an initial scan of the classes to identify what beans it is going to make. It will then start the 'initialisation' phase.

    @Bean annotated methods in @Configuration annotated classes will be invoked, and the result loaded into the ApplicationContext. So the RestHighLevelClient is created (by the method you have) and loaded.

    It then tries to create the ProfileService instance. It sees that a RestHighLevelClient instance is required (by constructor parameter). It looks in the ApplicationContext as well as the beans planned for creation in the scanning phase. Since there's only one RestHighLevelClient instance there's no conflict, and so that instance is used.


    From other comments:

    If there are multiple RestHighLevelClient instances either pending creation or already in the ApplicationContext then you will get a BeanCreationException detailing that 'too many candidates, expected 1 but found n'.

    These can be worked in several ways.

    You can annotate one of the RestHighLevelClient beans as @Primary which indicates 'use this if multiple are available, but only one required'.

    You can annotate the constructor parameter with an @Qualifier detailing which of the multiple instances to autowire.

    You can change the constructor parameter to a Collection<RestHighLevelClient> which will autowire all such instances, and then make the selection yourself in the constructor.