The next case does not work when cacheable behavior is desired
Bean A has methods with annotation @Cachable. Bean B a has dependency on Bean A and also Bean B implements org.springframework.beans.factory.FactoryBean and Spring initializes Bean B before enabling caching(when proxies are created for beans which methods have annotation @Cacheable)
The text was updated successfully, but these errors were encountered:
This can be an unfortunate consequence of early FactoryBean initialization, in this case triggered by a type check. In general, FactoryBean implementations should be considered a special-purpose solution and avoided in user components. Instead, we recommend @Bean methods instead if you're generally using the annotation-based component model.
As for your specific scenario, I suppose your FactoryBean implementation has either a dynamic getObjectType() implementation which returns null on a non-initialized instance, or it takes dependencies through a constructor? We generally recommend getObjectType() implementations with a best-effort static return type, even if it may determine a more specific return type dynamically after full initialization. And for lazy initialization purposes, it may make sense to preserve a no-arg constructor and take further dependencies through setter methods.
You could also declare the injection point where BeanB requires BeanA as @Lazy or as an ObjectProvider<Bean>, resulting in lazy initialization of that particular dependency on actual access. This would in particular help for a constructor-level injection point, not triggering full initialization of the dependency just for an early shortcut getObjectType() check.
Hi Juergen Hoeller.
It is Hybris project where all is set via spring xml. getObjectType() returns static Class value. My main purpose just to have a proxy which is able to choose the right implementation for an interface which is declared by getObjectType().
If you don't need BeanA for your getObjectType() implementation, declaring the injection point in the FactoryBean implementation as @Lazy BeanA or as ObjectProvider<BeanA> might be an easy way out, deferring its resolution until it is actually needed. For early initialization issues or circular dependency problems, declaring the affected injection points as lazy is highly recommended in general.
Juergen Hoeller Could you please show how bean should be set via spring xml if to use ObjectProvider? Maybe the issue is that not the bean directly is set to other bean but a map of beans, it throws now
Hmm, so it's an explicitly configured bean reference, not an @Autowired injection point... This makes it harder, unfortunately. You could declare the FactoryBean as BeanFactoryAware and perform lazy BeanFactory.getBean calls for the required target bean instances, configuring just the target bean name through an XML-driven setter method. That's a pattern that we use in quite a few of our own lazy-initialization delegates.
That said, I wonder why your FactoryBean gets fully initialized at such an early stage in the first place. If it responds with a non-null Class from the getObjectType() call, the container should proceed with a 'shortcut' instance that doesn't have the setters populated yet (and therefore doesn't resolve those setter-level dependencies yet). Maybe you could put a breakpoint into your getObjectType() method and see when and how often it is being called, and against which state of your FactoryBean instance? It would help to see the stackstraces at those points.