Skip to content

Subclassing RepositoryRestMvcConfiguration or SpringBootRepositoryRestMvcConfiguration causes EndpointWebMvcHypermediaConfiguration to fail with multiple ObjectMapper instances #3439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spencergibb opened this issue Jul 8, 2015 · 10 comments
Assignees
Milestone

Comments

@spencergibb
Copy link
Member

With 1.3.0.BUILD-SNAPSHOT using https://github.com/spring-cloud-samples/customers-stores

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaConfiguration$LinksConfiguration$MvcEndpointAdvice': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.fasterxml.jackson.databind.ObjectMapper org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaConfiguration$LinksConfiguration$MvcEndpointAdvice.mapper; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.fasterxml.jackson.databind.ObjectMapper] is defined: expected single matching bean but found 3: objectMapper,halObjectMapper,_halObjectMapper
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:766)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:819)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:522)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:677)
    at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:338)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:273)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:930)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:919)
    at example.customers.CustomerApp.main(CustomerApp.java:39)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.fasterxml.jackson.databind.ObjectMapper org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaConfiguration$LinksConfiguration$MvcEndpointAdvice.mapper; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.fasterxml.jackson.databind.ObjectMapper] is defined: expected single matching bean but found 3: objectMapper,halObjectMapper,_halObjectMapper
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:571)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 17 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.fasterxml.jackson.databind.ObjectMapper] is defined: expected single matching bean but found 3: objectMapper,halObjectMapper,_halObjectMapper
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1073)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:961)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:543)
    ... 19 more
@wilkinsona wilkinsona self-assigned this Jul 8, 2015
@wilkinsona
Copy link
Member

The problem's triggered by the sample's RepositoryRestMvcConfiguration subclass (Aside: that should be a SpringBootRepositoryRestMvcConfiguration subclass in a Spring Boot app). This subclass pulls in the definition for Spring Data REST's ObjectMapper. This means that there's an ObjectMapper bean before Boot's JacksonAutoConfiguration has run. This prevents it from creating its @Primary ObjectMapper leading to the failure due to multiple ObjectMapper beans.

The problem can be avoided by removing the extension of RepositoryRestMvcConfiguration. I think the configuration can then be applied via @PostConstruct. That's certainly sufficient to get the two apps to start, but I've been unable to test fully as I can't get the config server to build and run from source. I've submitted a pull request with the changes.

In the longer term, I think we need some changes to Spring Data REST so that the configuration can be customised without having to a extend a class that contains bean definitions. An approach similar to Spring MVC's WebMvcConfigurer and WebMvcConfigurerAdapter, perhaps. /cc @olivergierke.

@wilkinsona wilkinsona changed the title EndpointWebMvcHypermediaConfiguration fails with 3 ObjectMapper instances Subclassing RepositoryRestMvcConfiguration or SpringBootRepositoryRestMvcConfiguration causes EndpointWebMvcHypermediaConfiguration to fail with multiple ObjectMapper instances Jul 8, 2015
izeye added a commit to izeye/samples-spring-boot-branches that referenced this issue Jul 13, 2015
@izeye
Copy link
Contributor

izeye commented Jul 13, 2015

I have the same problem and @wilkinsona 's workaround works for me.

I guess the following guide needs to be changed due to this problem if it won't be fixed:

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-use-exposing-spring-data-repositories-rest-endpoint

@bedge
Copy link

bedge commented Jul 14, 2015

Is there a workaround to attach a configureValidatingRepositoryEventListener if one cannot subclass a RepositoryRestMvcConfiguration?
Or, any kind of beforeSave... listener for that matter?

ie:

public class Application extends RepositoryRestMvcConfiguration {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
        validatingListener.addValidator("beforeCreate", new PeopleValidator());
    }
}

(from http://stackoverflow.com/questions/24318405/spring-data-rest-validator)

@bedge
Copy link

bedge commented Jul 14, 2015

I was able to work around this using

@Configuration
public class CollectionValidatorRegistrar implements InitializingBean {

        @Autowired
        ListableBeanFactory beanFactory;

        @Autowired
        ValidatingRepositoryEventListener validatingRepositoryEventListener;

        @Autowired CollectionValidator collectionValidator;
        @Override
        public void afterPropertiesSet() throws Exception {
            validatingRepositoryEventListener.addValidator("beforeSave", collectionValidator);
        }

}

@wilkinsona
Copy link
Member

I've discussed this with @olivergierke and opened DATAREST-621 which he hopes to tackle in time for Gosling RC1.

@wilkinsona wilkinsona added this to the 1.3.0.RC1 milestone Jul 23, 2015
@bodiam
Copy link

bodiam commented Sep 2, 2015

Hi all, I'm suffering from the same issue in 1.3.0.M4. I'm subclassing RepositoryRestMvcConfiguration instead of SpringBootRepositoryRestMvcConfiguration. I understand now I shouldn't probably do this, and I'll migrate to the new way of doing things, but having a main application like this already triggers this issue:

@SpringBootApplication
public class Application extends RepositoryRestMvcConfiguration  {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@odrotbohm
Copy link
Member

The suggested workaround is extending RepositoryRestConfigurerAdapter introduced with Spring Data release train Gosling. Boot snapshots have already upgraded to that. Would you mind giving this a spin?

@bodiam
Copy link

bodiam commented Sep 2, 2015

Sure, I can try that. Is it a drop in replacement?

update: Okay, I'm now extending the RepositoryRestConfigurerAdapter, that works! Thanks!

But if extending the RepositoryRestMvcConfiguration gives such issues, then what's the purpose of allowing to extend it?

@Juchar
Copy link

Juchar commented Dec 17, 2015

By consequence it is also not possible to override RepositoryRestMvcConfiguration#resourceDescriptionMessageSourceAccessor to customize the location of the property files, correct?

@wilkinsona
Copy link
Member

@Juchar Correct. If that can't be customised via a RespositoryRestConfigurerAdapter subclass then you should raise a Spring Data REST enhancement request to improve its configuration model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants