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

@Cacheable does not make sense when bean is depended by other bean which class implements FactoryBean [SPR-17471] #22003

Closed
spring-projects-issues opened this issue Nov 6, 2018 · 5 comments
Assignees
Labels
in: core status: declined

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Nov 6, 2018

Nikolas Andr opened SPR-17471 and commented

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)


Affects: 4.3.20

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 6, 2018

Juergen Hoeller commented

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.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 6, 2018

Nikolas Andr commented

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(). 

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 6, 2018

Juergen Hoeller commented

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.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 6, 2018

Nikolas Andr commented

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

 

Cannot convert value of type [com.l.DefaultProductBuyableStrategy] to required type [org.springframework.beans.factory.ObjectProvider] for property 'implMapping[site-mx]': no matching editors or conversion strategy found

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 6, 2018

Juergen Hoeller commented

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.

@spring-projects-issues spring-projects-issues added type: bug status: declined in: core labels Jan 11, 2019
@spring-projects-issues spring-projects-issues removed the type: bug label Jan 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core status: declined
Projects
None yet
Development

No branches or pull requests

2 participants