Skip to content

Unclear error message on BeanPostProcessor returning null [SPR-11951] #15997

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
spring-projects-issues opened this issue Jul 4, 2014 · 4 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

Benoit Lacelle opened SPR-11951 and commented

Hello,

If one has a BeanPostProcessor which returns null, then the laoding of the application contact would break with an exception not clear before knowing the actual issue.

Running the following test, :

	@Configuration
	public static class ConfigA {
		@Bean
		public String string() {
			return "gogo";
		}
	}

	@Configuration
	public static class ConfigB {
		@Bean
		public BeanPostProcessor beanPostProcessor() {
			return new BeanPostProcessor() {

				@Override
				public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
					return null;
				}

				@Override
				public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
					return null;
				}

			};
		}
	}

	@Test
	public void testInvalidConfig() {
		try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigA.class, ConfigB.class)) {

		}
	}

one would get

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'string' defined in class blasd.apex.server.config.spring.autonomy.TestApexSourceConfigAutonomy$ConfigA: factory-bean 'testApexSourceConfigAutonomy.ConfigA' returned null
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:372)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
	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:703)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
	at blasd.apex.server.config.spring.autonomy.TestApexSourceConfigAutonomy.testInvalidConfig(TestApexSourceConfigAutonomy.java:118)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

This seems trivial, but I got an equivalent issue while using Mockito to mock an interface, which I was unaware to be a BeanPostProcessor: Mockito returned null on postProcessBeforeInitialization, leading to this issue but the error message (and even debugging) took me a few hours to understand what was happening.

Instead of "Error creating bean with name 'string' defined in class blasd.apex.server.config.spring.autonomy.TestApexSourceConfigAutonomy$ConfigA: factory-bean 'testApexSourceConfigAutonomy.ConfigA' returned null", I would have expected something like "BeanPostProcessor named "beanPostProcessor" returned null for 'testApexSourceConfigAutonomy.ConfigA'"

Thanks


Affects: 3.2.9, 4.0.3

Referenced from: commits 83a7deb, 077c624, 6c41cc3

Backported to: 3.2.10

@spring-projects-issues
Copy link
Collaborator Author

Stéphane Nicoll commented

Your PostProcessor does not seem very realistic to me. You are basically saying that any bean that the context processes should be nullified. This includes Spring's own internal beans that it registers in the context. In the case of your particular test, you are setting org.springframework.context.annotation.ConfigurationClassPostProcessor to null. So you're basically preventing the @Configuration classes to be processed at all.

If you change your PostProcessor to nullify what it needs, this just work as it should, i.e.

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	if (beanName.equals("string")) {
		return null;
	}
	return bean;
}

Then your context start and you get a null "string" bean.

There's nothing wrong to return null there. In certain circumstances (i.e. factory bean), that might give weird results but I am not sure we can do something about it: the method that calls the post processor has no knowledge about the bean (and it shouldn't).

@spring-projects-issues
Copy link
Collaborator Author

Benoit Lacelle commented

Hi Stéphane,

I agree my BeanPostProcessor is not relevant. It was just for the sake of the unit-test readablity.

My usecase is less trivial: I have some interface like the following:

public static interface SomeBean extends BeanPostProcessor {
		void doSomething();
	}

I don't own it, so I did not know it was a BeanPostProcessor.

My actual unit-test was like the following:

@Configuration
	public static class ConfigA {
		@Bean
		public String string() {
			return "gogo";
		}
	}

	@Configuration
	public static class ConfigB {
		@Bean
		public SomeBean someBean() {
			return Mockito.mock(SomeBean.class);
		}
	}

	@Test
	public void testInvalidConfig() {
		try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigA.class, ConfigB.class)) {

		}
	}

Given the error message, I was not lead to understand SomeBean was the BeanPostProcessor returning null.

Maybe we should check ConfigurationClassPostProcessor beans are never turned to null.

My question was rather: could we improve the error message?

By 'Error creating bean with name 'string' defined in class blasd.apex.server.config.spring.autonomy.TestApexSourceConfigAutonomy$ConfigA: factory-bean 'testApexSourceConfigAutonomy.ConfigA' returned null', I understand that "testApexSourceConfigAutonomy.ConfigA" returned a null instance. While the issue is "'beanPostProcessor' returned a null instance for 'testApexSourceConfigAutonomy.ConfigA'"

@spring-projects-issues
Copy link
Collaborator Author

Benoit Lacelle commented

I re-open just to be sure if it is possible to improve the error message. I agree it works as designed.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

I've reworded the exception's message to

"factory-bean 'myBean' (or a BeanPostProcessor involved) returned null"

We don't clearly know why we got a null result at that point of processing (since we're obtaining another bean instance there, constructed completely independently, with no context transferred to us)... but we can at least point out that a BeanPostProcessor may have been involved.

Juergen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants