Skip to content

FactoryBean bean type detection can causes fatal early instantiation [SPR-11202] #15828

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 Dec 10, 2013 · 1 comment
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Dec 10, 2013

Dave Syer opened SPR-11202 and commented

The BeanFactory.getBeanNamesForAnnotation() method has been causing issues. Here's a test case:

public class BeansWithAnnotationTests {

	private AnnotationConfigApplicationContext context;

	@After
	public void close() {
		if (context != null) {
			context.close();
		}
	}

	@Test // Fails
	public void testWithImporter() {
		context = new AnnotationConfigApplicationContext(Wrapper.class);
		assertEquals("foo", context.getBean("value"));
	}
	
	@Test // Passes
	public void testWithoutImporter() {
		context = new AnnotationConfigApplicationContext(Config.class);
		assertEquals("foo", context.getBean("value"));
	}
	
	@Configuration
	@Import(Selector.class)
	protected static class Wrapper {
		
	}
	
	protected static class Selector implements ImportSelector {

		@Override
		public String[] selectImports(AnnotationMetadata importingClassMetadata) {
			return new String[] {Config.class.getName() };
		}
		
	}

	@Configuration
	protected static class Config {

		public Config() {
			// Just so I can put a breakpoint here
			System.getProperty("foo");
		}

		@Bean
		public FooFactoryBean foo() {
			return new FooFactoryBean();
		}

		@Bean
		public String value() throws Exception {
			String name = foo().getObject().getName();
			Assert.state(name != null, "Name cannot be null");
			return name;
		}

		@Bean
		@Conditional(NoBarCondition.class)
		public String bar() throws Exception {
			return "bar";
		}

	}

	protected static class NoBarCondition implements Condition {

		@Override
		public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
			if (context.getBeanFactory().getBeanNamesForAnnotation(Bar.class).length > 0) {
				return false;
			}
			return true;
		}

	}

	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	@Target(ElementType.TYPE)
	protected static @interface Bar {
	}

	protected static class FooFactoryBean implements FactoryBean<Foo>, InitializingBean {

		private Foo foo = new Foo();

		@Override
		public Foo getObject() throws Exception {
			return foo;
		}

		@Override
		public Class<?> getObjectType() {
			return Foo.class;
		}

		@Override
		public boolean isSingleton() {
			return true;
		}

		@Override
		public void afterPropertiesSet() throws Exception {
			this.foo.name = "foo";
		}

	}

	protected static class Foo {

		private String name;

		public String getName() {
			return name;
		}

	}

}

You need a perfect storm of a FactoryBean as a @Bean inside a @Configuration that was ImportSelected for this to break (I tried quite a few other scenarios). The passing test works because Spring doesn't try to instantiate the Config object.


Affects: 4.0 RC2

Issue Links:

Referenced from: commits 106a973

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

It looks like there is an easy enough solution, blocking the early creation of FactoryBeans for type checking purposes if they are coming from a factory method on yet another bean. Basically it's whether "freezeConfiguration()" happened already: Before that point, we'll be conservative; afterwards we'll proceed as usual with respect to FactoryBean initialization. Condition evaluation happens before, whereas regular autowiring happens afterwards.

This makes your test pass, and is hopefully generally a fine measure.

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) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants