tags:

views:

26

answers:

2

I'm using Spring's ObjectFactoryCreatingFactoryBean to retrieve a prototype scoped bean, as described in the Spring Documentation. Below is my applicationContext.

<bean id="exportFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
    <property name="targetBeanName">
        <idref local="export" />
    </property>
</bean>

<bean id="export" class="com.someorg.ExportImpl" scope="prototype"/>

I autowire the exportFactory into a class like so:

@Autowired
@Qualifier("exportFactory")
private ObjectFactory<?> exportFactory;

And this works as expected. Each call to the exportFactory.getObject() method returns a new ExportImpl. On further inspect, a call to getObject() actually returns the following instance: org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean$TargetBeanObjectFactory

Now, ExportImpl is an implementation of an Export interface. And when I attempt to to declare the exportFactory using generics, described below, I get an exception.

@Autowired
@Qualifier("exportFactory")
private ObjectFactory<Export> exportFactory;

Stacktrace:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.someorg.Export] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=exportFactory)}

The application context successfully loads with this configuration, and the exception is thrown when I call exportFactory.getObject(). Using the same configuration I can successfully retrieve an instance of ExportImpl, so I know that bean is correctly wired.

I'd like to know a) what is Spring doing here and b) is there a reason I'm unable to use an ObjectFactory with type parameter that's an interface?

+1  A: 

Change the qualifier to:

@Qualifier("export")
Bozho
Yeah, that was just me simplifying and forgetting to change my pasted code. The working code has the correct qualifier.
unsquared
@unsquared - no, your exception tells otherwise - `Qualifier(value=exportFactory)}`
Bozho
@Bozho, I was copying and pasting into notepad, before posting to SO. I changed the value in the xml and stacktrace, but forgot the qualifier.
unsquared
@unsquared - well, it is working for me with the correct qualifier. So double-check that you've had the correct qualifier, and give the corresponding exception message
Bozho
@Bozho, indeed you're right. Stupid mistake. I've accepted axtavt's answer because this won't help anybody who is less brain dead than me, and deals with an interesting Spring feature.
unsquared
@Bozho, ah, this answer is also correct and interesting. Can I accept both?
unsquared
@unsquared you can't accept both. Let axtavt's be accepted - he gives more insight into the problem. Or perhaps provide your own answer, stating that changing the schema fixed the problem, and accept it.
Bozho
+1  A: 

It turns out that ObjectFactoryCreatingFactoryBean is not necessary when you obtain ObjectFactory via @Autowired. In this case ObjectFactory for your bean is created automatically, though I can't find any reference of this behaviour in the documentation.

So, the behaviour you observe can be explained as follows:

  • When you write @Autowired @Qualifier("exportFactory") ObjectFactory<?>, Spring creates ObjectFactory that returns a bean named exportFactory, which itself is an ObjectFactory returned by the ObjectFactoryCreatingFactoryBean (its class is org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean$TargetBeanObjectFactory).

  • When you write @Autowired @Qualifier("exportFactory") ObjectFactory<Export>, Spring tries to find bean of type Export named exportFactory, and the search fails.

axtavt
This would explain why my call to exportFactory().getObject() was returning an instance of ObjectFactoryCreatingFactoryBean$TargetBeanObjectFactory, which would then, upon casting, return the expected ExportImpl.
unsquared
Interestingly enough, I had the spring-context-2.5.xsd, and the spring-beans-2.5.xsd while running spring-3.0.2. I changed the schema locations to 3.0, and the problem went away.
unsquared
And yes, commenting out the bean with id="exportFactory" has also done the trick. Thanks.
unsquared
@axtavt - If you use the @Qualifier describing by Bozho, you can retain @Autowired and Spring in fact returns the expected bean.
unsquared
@unsquared: Yes, because in this case Spring looks for the bean of type `Export` named `export`, and successfully finds it, ignoring the unnecessary `exportFactory`.
axtavt
@axtavt - Yup. It is a bit counter-intuitive though. Would @Resource("exportFactory") work? I'm going to test that out.
unsquared