spring-projects / spring-hateoas

Spring HATEOAS - Library to support implementing representations for hyper-text driven REST web services.
https://spring.io/projects/spring-hateoas
Apache License 2.0
1.04k stars 478 forks source link

ConverterRegisteringWebMvcConfigurer#0: defined in null, overriding RestTemplate #779

Open drenda opened 5 years ago

drenda commented 5 years ago

I'm facing a weird exception involving ConverterRegisteringWebMvcConfigurer.

I posted my problem here.

I tried all things I could think about without any luck. I'm not sure it's a bug, but for sure the error message is quite weird:

`Field restTemplate in cloud.test.server.rest.clients.TenantRestClient required a single bean, but 2 were found:

As described in my question, my code works until Spring Boot 2.0.3.

gregturn commented 5 years ago

You may be rigging together a little differently than expected.

The idea of RestTemplateBuilder is to inject that bean into your code, and then "get" the a RestTemplate from it and use it right away. You're creating one then using it to register a RestTemplate bean.

I don't know what the @EnableRetry and other annotations might be doing that your code is getting in the way of.

You might want to back off of registering a RestTemplate into the app context and instead, autowire the RestTemplateBuilder into the target, building your RestTemplate there.

drenda commented 5 years ago

Thanks for your reply @gregturn . I would really like to do what you explained (it was the first thing I did), but I arrived to that weird solution because of this:

The dependencies of some of the beans in the application context form a cycle:

   webSecurityConfiguration
      ↓
   org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
      ↓
   webMvcConfiguration (field private cloud.test.server.rest.clients.TenantRestClient cloud.test.server.config.WebMvcConfiguration.tenantRestClient)
┌─────┐
|  tenantRestClient defined in file [/Users/drenda/Documents/workspaceREST/test-server/target/classes/cloud/test/server/rest/clients/TenantRestClient.class]
↑     ↓
|  restTemplateBuilder defined in class path resource [org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.class]
↑     ↓
|  org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
↑     ↓
|  jacksonHttpMessageConverter defined in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.class]
↑     ↓
|  repositoryRestConfiguration defined in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.class]
↑     ↓
|  repositories defined in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.class]
↑     ↓
|  documentRepository
↑     ↓
|  (inner bean)#1ae90cb9
↑     ↓
|  entityManagerFactory defined in class path resource [cloud/test/server/config/HibernateConfig.class]
↑     ↓
|  multiTenantConnectionProviderImpl (field private cloud.test.server.tenants.ConnectionPoolManager cloud.test.server.tenants.MultiTenantConnectionProviderImpl.connectionPoolManager)
↑     ↓
|  connectionPoolManagerImpl (field private cloud.test.server.rest.clients.TenantRestClient cloud.test.server.tenants.ConnectionPoolManagerImpl.tenantRestClient)
└─────┘

In short: this is a multi-tenant application and in multiTenantConnectionProviderImpl I need TenantRestClient that use a RestTemplate to get DB infos for a central system. I would like to get advantage of Spring's restTemplate in order to have messageConverters and exceptions handling set properly.

Thanks

gregturn commented 5 years ago

I'm honestly not sure how to disentangle your situation. You are mixing together a lot of technologies and the result is we can't track down a circular dependency of epic proportion.

Also, since you are using Spring Data REST, the problem may have nothing to do with Spring HATEOAS but instead custom code in Spring Data REST.

fenrivymor commented 5 years ago

@drenda Hello, I'm receiving the same error, did you solved?


APPLICATION FAILED TO START


Description:

Field restTemplate in com.mycompany.services.rest.RestService required a single bean, but 2 were found:

KISTERS-Development-LA commented 5 years ago

I think it is a bug in org.springframework.hateoas.config.HypermediaSupportBeanDefinitionRegistrar. When I debuged it, this class will be registered in two times.

This error only occurs when Resttemplate is registered as Bean in ApplicationContext. See: org.springframework.hateoas.config.ConverterRegisteringBeanPostProcessor#postProcessBeforeInitialization

The postProcessBeforeInitialization tries to find the configurer and get this bean two times.

I fixed it very quick&dirty, by editing the registerSourcedBeanDefinition method: ` private static String registerSourcedBeanDefinition( BeanDefinitionBuilder builder, AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
String generateBeanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, registry);
if (StringUtils.equals(
    generateBeanName,
    "org.springframework.hateoas.config.ConverterRegisteringWebMvcConfigurer#0")) {
  return registerSourcedBeanDefinition(builder, metadata, registry, generateBeanName);
}
return "";

} `

@gregturn will this help to fix this bug?

GuiRitter commented 5 years ago

I've had this problem as well, and I think I solved it by adding spring.main.allow-bean-definition-overriding=true to application.properties.

EDIT: Sorry, that wasn't it. That was for a conflict involving an ObjectMapper and a CsvMapper. I suspect it may still work for RestTemplate, though. Anyway, here's how I'm building my RestTemplate. It could be useful.

@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
    RestTemplate restTemplate = restTemplateBuilder.build();
    mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(APPLICATION_JSON, APPLICATION_OCTET_STREAM));
    restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
    return restTemplate;
}
Saran-Agiliz commented 5 years ago

Facing similiar problem with below Exception

Field s2sClient_ in com.xxx.yyy.xxx.Class required a single bean, but 8 were found:

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

fenrivymor commented 5 years ago

Solved! I excluded a repeated declaration of ConverterRegisteringWebMvcConfigurer by removing redundant dependency declaration.

ken4ward commented 5 years ago

Solved! I excluded a repeated declaration of ConverterRegisteringWebMvcConfigurer by removing redundant dependency declaration.

@fabiohenriquebayma could you Kindly explain the redundancy dependency you removed? I need a fix for the issue as well.

fenrivymor commented 5 years ago

Solved! I excluded a repeated declaration of ConverterRegisteringWebMvcConfigurer by removing redundant dependency declaration.

@fabiohenriquebayma could you Kindly explain the redundancy dependency you removed? I need a fix for the issue as well.

I just removed the declaration of spring-boot-starter-web in favor of spring-hateoas. spring-hateoas has spring-web. I could also exclude spring-web from heteoas in favor of spring-boot-starter-web. I believe it is case-by-case.

atomici commented 4 years ago

I solved the problem by using RestTemplateBuilder instead of RestTemplate. Before I removed my @Bean RestTemplate from configuration file.

 private RestTemplate restTemplate;
   public MyClass(RestTemplateBuilder restTemplate){
     this.restTemplate = restTemplate.build();
    }