Skip to content
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

BeanPostProcessor bean declaration that retrieves object from injected ObjectFactory with bounded type parameter results in missing PluginRegistry bean during application startup #1210

Open
VTLob opened this issue Feb 16, 2020 · 3 comments
Labels
in: configuration Configuration and setup type: bug

Comments

@VTLob
Copy link

VTLob commented Feb 16, 2020

Given:

  • a Spring-Boot project
  • using Spring-HATEOAS

When:

  • there is a BeanPostProcessor bean declaration
  • which gets an ObjectFactory with bounded type parameter injected
  • where getObject() is called on the injected ObjectFactory

Then:

  • application startup fails with error Parameter 0 of method _relProvider in org.springframework.hateoas.config.HateoasConfiguration required a bean of type 'org.springframework.plugin.core.PluginRegistry' that could not be found..

The following example bean declaration will cause the error:

@Bean
public BeanPostProcessor exampleBeanPostProcessor(ObjectFactory<? extends ExampleComponent> exampleComponentObjectFactory) {
    exampleComponentObjectFactory.getObject();
    return new BeanPostProcessor() {};
}

Any of the following alterations to the bean declaration will avoid the error:

  • using a non-bounded type parameter
  • not calling getObject() on the ObjectFactory
  • declaring a bean of a type not BeanPostProcessor

Complete error message:

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

Description:

Parameter 0 of method _relProvider in org.springframework.hateoas.config.HateoasConfiguration required a bean of type 'org.springframework.plugin.core.PluginRegistry' that could not be found.

The following candidates were found but could not be injected:
	- User-defined bean method 'relProviderPluginRegistry' in 'HateoasConfiguration'


Action:

Consider revisiting the entries above or defining a bean of type 'org.springframework.plugin.core.PluginRegistry' in your configuration.

Minimal example project: https://github.com/VTLob/spring-hateoas-issue

@odrotbohm
Copy link
Member

odrotbohm commented Feb 16, 2020

The linked project is inaccessible currently.

Generally speaking, BeanPostProcessor instances are resolved very early n the application lifecycle as they – as the name suggests – are supposed to post process bean instances during their creation. If you actively access a user defined component during the construction of the BPP, you're inevitably asking for trouble as the container has to inspect bean definitions right away and might prematurely initialize beans.

So a couple of questions:

  1. A BPP requiring a user component is pretty unusual. Can you elaborate on what your actual use case is? It's very likely that there's a different, less bootstrap lifecycle invasive solution.
  2. If the BPP really needs the user component, it should be injected using an ObjectProvider (or ObjectFactory) wrapper. Calling ….getObject() in the @Bean doesn't make sense as you could've just injected ExampleComponent right away.
  3. Do you have a complete stack trace that shows the initialization order?

@VTLob
Copy link
Author

VTLob commented Feb 16, 2020

The given example is just the issue reduced to its minimum. The actual BPP that caused this issue is used to bind MeterBinder instances to a MeterRegistry instance which was created before them due to being injected in another bean (which results in some metrics not being reported). This worked fine until we tried to add Spring-HATEOAS to our project.
I adjusted the BPP to not expect an ObjectProvider<? extends MeterRegistry> but ObjectProvider<MeterRegistry> instead so on our side so we are fine now. The original issue remains though.

Full stack trace:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'repositorySearchController' defined in URL [jar:file:/~/.m2/repository/org/springframework/data/spring-data-rest-webmvc/3.2.1.RELEASE/spring-data-rest-webmvc-3.2.1.RELEASE.jar!/org/springframework/data/rest/webmvc/RepositorySearchController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration': Unsatisfied dependency expressed through field 'relProvider'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '_relProvider' defined in class path resource [org/springframework/hateoas/config/HateoasConfiguration.class]: Unsatisfied dependency expressed through method '_relProvider' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.plugin.core.PluginRegistry<org.springframework.hateoas.server.LinkRelationProvider, org.springframework.hateoas.server.LinkRelationProvider$LookupContext>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:787)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:226)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1358)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
	at de.vtlob.spring.hateoas.issue.ExampleApplication.main(ExampleApplication.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration': Unsatisfied dependency expressed through field 'relProvider'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '_relProvider' defined in class path resource [org/springframework/hateoas/config/HateoasConfiguration.class]: Unsatisfied dependency expressed through method '_relProvider' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.plugin.core.PluginRegistry<org.springframework.hateoas.server.LinkRelationProvider, org.springframework.hateoas.server.LinkRelationProvider$LookupContext>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:397)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1429)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:400)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1287)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:874)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:778)
	... 24 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '_relProvider' defined in class path resource [org/springframework/hateoas/config/HateoasConfiguration.class]: Unsatisfied dependency expressed through method '_relProvider' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.plugin.core.PluginRegistry<org.springframework.hateoas.server.LinkRelationProvider, org.springframework.hateoas.server.LinkRelationProvider$LookupContext>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:787)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:528)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory$2.resolveCandidate(DefaultListableBeanFactory.java:1738)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1287)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.createOptionalDependency(DefaultListableBeanFactory.java:1741)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1194)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:636)
	... 47 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.plugin.core.PluginRegistry<org.springframework.hateoas.server.LinkRelationProvider, org.springframework.hateoas.server.LinkRelationProvider$LookupContext>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:874)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:778)
	... 62 common frames omitted

@odrotbohm
Copy link
Member

First of all, glad you found a workaround.

The actual BPP that caused this issue is used to bind MeterBinder instances to a MeterRegistry instance which was created before them due to being injected in another bean (which results in some metrics not being reported).

I am afraid you lost me on that one but I am not an expert in Micrometer. I'd definitely would keep the ObjectFactory/ObjectProvider unresolved for as long as possible. BPP implementations are often only interested in beans of a given type, so I'd make sure I'd only call ….getObject() if a bean of that type is actually found.

I adjusted the BPP to not expect an ObjectProvider<? extends MeterRegistry> but ObjectProvider instead so on our side so we are fine now.

That fact that this seems to make a difference is odd. @jhoeller, does that ring a bell? The sample linked to above is really minimal and if you tweak the generics declaration you can see this switching from does not work to work reliably.

@odrotbohm odrotbohm added in: configuration Configuration and setup type: bug labels Feb 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: configuration Configuration and setup type: bug
Projects
None yet
Development

No branches or pull requests

2 participants