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.03k stars 477 forks source link

Spring Hateoas prevents from disabling 'allowBeanDefinitionOverriding' #640

Open tvahrst opened 7 years ago

tvahrst commented 7 years ago

We had some trouble with overridden spring beans in one of our spring boot applications. So we decided to set spring-context's 'allowBeanDefinitionOverriding' Flag to false to determine the duplicate bean definitions of our application.

But this leads to an Exception during spring-boot startup, caused by Spring Hateoas (we use spring-hateos to autogenerate the actuator endpoint overview list). The exception:


org.springframework.beans.factory.BeanDefinitionStoreException:
--
Invalid bean definition with name 'entityLinksPluginRegistry' defined in null:
Cannot register bean definition [Root bean: class [org.springframework.plugin.core.support.PluginRegistryFactoryBean];
scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'entityLinksPluginRegistry':
There is already [Root bean: class [org.springframework.plugin.core.support.PluginRegistryFactoryBean];
scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
 
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:814)
at org.springframework.hateoas.config.LinkBuilderBeanDefinitionRegistrar.registerBeanDefinitions(LinkBuilderBeanDefinitionRegistrar.java:63)
at org.springframework.hateoas.config.HypermediaSupportBeanDefinitionRegistrar.registerBeanDefinitions(HypermediaSupportBeanDefinitionRegistrar.java:100)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:359)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:143)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:116)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:320)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:270)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:93)

The bean definition 'entityLinksPluginRegistry' is definined in LinkBuilderBeanDefinitionRegistrar, and this Registrar is called twice:

  1. From HypermediaAutoConfiguration#EntityLinksConfiguration via @EnableEntityLinks. This Annoation imports the LinkBuilderBeanDefinitionRegistrar.

  2. From HypermediaAutoConfiguratoin#HypermediaConfiguration via @EnableHypermediaSupport. This Annoations imports HypermediaSupportBeanDefinitionRegistrar. This registrar in turn creates its own Instance of LinkBuilderBeanDefinitionRegistrar and invokes 'registerBeanDefinitions()'. This second invocation leads to overriding all allready defined Beandefinitions.

Possible Solution: check in LinkBuilderBeanDefinitionRegistrar if the beans are already defined and avoid duplicate registration.

gregturn commented 5 years ago

Can you check if this still an issue in 1.0.0.BUILD-SNAPSHOT?

tvahrst commented 5 years ago

The current Spring-Boot dependency management (Spring Boot 2.1.x) uses spring-hateoas in version 0.25.0.RELEASE. With 0.25.0.RELEASE the error does not occur. ( Spring Boot 2.1 disables bean overriding by default, (https://github.com/spring-projects/spring-booT/wiki/Spring-Boot-2.1-Release-Notes) so the error seems to be fixed in 0.25.0.RELEASE.

I made a short Test, setting up a Spring-Appl with spring.initializr with actuator, hateoas und changed the spring-hateoas version to 1.0.0.BUILD-SNAPSHOT. During start of application, I get

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method linkDiscoverers in org.springframework.hateoas.config.HateoasConfiguration required a single bean, but 3 were found:
    - entityLinksPluginRegistry: defined by method 'entityLinksPluginRegistry' in class path resource [org/springframework/hateoas/config/EntityLinksConfiguration.class]
    - relProviderPluginRegistry: defined by method 'relProviderPluginRegistry' in class path resource [org/springframework/hateoas/config/HateoasConfiguration.class]
    - linkDiscovererRegistry: defined in null

My pom.xml dependencies:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.hateoas</groupId>
                <artifactId>spring-hateoas</artifactId>
                <version>1.0.0.BUILD-SNAPSHOT</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

    </dependencies>

Snapshot version: 1.0.0.BUILD-2019-02-07.192213-57