0

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()?

lepe
  • 24,677
  • 9
  • 99
  • 108
  • 1
    It looks for autowire candidates (instances of `RestHighLevelClient`) in the `ApplicationContext`, finds only one (no other instances pending creation, and one has already been made by the `@Bean` annotated method). Since there's only one, it gets used. – BeUndead Feb 12 '20 at 01:20
  • `@Bean` annotated methods in `@Configuration` annotated classes will be invoked and the result loaded into the `ApplicationContext` (in case the confusion was there instead). – BeUndead Feb 12 '20 at 01:23
  • So, in case two or more methods inside any `@Configuration` component Spring return the same kind of object, will it report it? (as a warning or error?) – lepe Feb 12 '20 at 01:25
  • They can have multiple methods. If multiple methods returned an instance of `RestHighLevelClient` then it would fail to create the `ProfileService` (too many candidates). However you can get around this by marking one of them as `@Primary` (specifying this is preferred when there's multiple), or by specifying which one you want with an `@Value("beanName")` annotation on the parameter in your `ProfileService` constructor (by default the 'beanName' will be the same as the method name. But you can specify this in the `@Bean` annotation). – BeUndead Feb 12 '20 at 01:27
  • 1
    @BeUndead: Thanks! your explanation is very clear to me. I suspected something like that, but I was not sure. Can you move your comments into an answer so I can accept it, please? – lepe Feb 12 '20 at 01:32
  • Done. Sorry, I wanted to work out which aspect wasn't clear before writing one first. :) – BeUndead Feb 12 '20 at 01:39

1 Answers1

1

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.

BeUndead
  • 3,463
  • 2
  • 17
  • 21