Skip to content

MethodInvokingFactoryBean not well-suited for non-factory methods [SPR-11196] #15822

@spring-projects-issues

Description

@spring-projects-issues

Benoit Lacelle opened SPR-11196 and commented

If a bean holds an @Autowire field, the whole application context seems to be initialized early to check for the type of all beans.

I would expect this eager initialisation phase not to actually instantiate the beans, in order to rely on the default bean ordering.

This seems not to be the case when considering some FactoryBean, at least MethodInvokingFactoryBean.

In my case, the MethodInvokingFactoryBean has been loaded right before the @Autowire field initialisation (when it is suppose to happen much later).

it ssems to be done so as, since this been is not configured yet: MethodInvokingFactoryBean

/**
	 * Return the type of object that this FactoryBean creates,
	 * or <code>null</code> if not known in advance.
	 */
	public Class<?> getObjectType() {
		if (!isPrepared()) {
			// Not fully initialized yet -> return null to indicate "not known yet".
			return null;
		}
		return getPreparedMethod().getReturnType();
	}

Then, it instanciates the underlying singleton bean (i.e. it executes the method):

DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 225
DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 291
DefaultListableBeanFactory(AbstractBeanFactory).getTypeForFactoryBean(String, RootBeanDefinition) line: 710

I would expect AbstractBeanFactory.doGetBean to check for typeCheckOnly when calling

sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
					public Object getObject() throws BeansException {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					}
				});

like in:

if (!typeCheckOnly) {
	markBeanAsCreated(beanName);
}

and in this case, it would not execute the method.

In my case, it lead to a hard to spot issue: as the method execution failed, the autowiring logic simply rejected this bean as a candidate. Later, the methodInvokingFactoryBean is executed again, but it failed for an obscure reason as the first (failed) call has changed some internal state.

Here is a stack leasing to the first method call:

MyProjectClass(MyProjectClass).myProjectMethod(String) line: myProjectLine
                NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]    
                NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39  
                DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25         
                Method.invoke(Object, Object...) line: 597        
                MethodInvokingFactoryBean(MethodInvoker).invoke() line: 273           
                MethodInvokingFactoryBean.doInvoke() line: 162         
                MethodInvokingFactoryBean.afterPropertiesSet() line: 152      
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).invokeInitMethods(String, Object, RootBeanDefinition) line: 1514
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1452
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 519          
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 456          
                AbstractBeanFactory$1.getObject() line: 294     
                DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 225         
                DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 291    
                DefaultListableBeanFactory(AbstractBeanFactory).getTypeForFactoryBean(String, RootBeanDefinition) line: 1356               
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).getTypeForFactoryBean(String, RootBeanDefinition) line: 710  
                DefaultListableBeanFactory(AbstractBeanFactory).isTypeMatch(String, Class<?>) line: 519        
                DefaultListableBeanFactory.doGetBeanNamesForType(Class<?>, boolean, boolean) line: 339 
                DefaultListableBeanFactory.getBeanNamesForType(Class<?>, boolean, boolean) line: 316       
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean) line: 187               
                DefaultListableBeanFactory.findAutowireCandidates(String, Class<?>, DependencyDescriptor) line: 857           
                DefaultListableBeanFactory.doResolveDependency(DependencyDescriptor, Class<?>, String, Set<String>, TypeConverter) line: 814            
                DefaultListableBeanFactory.resolveDependency(DependencyDescriptor, String, Set<String>, TypeConverter) line: 731        
                AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(Object, String, PropertyValues) line: 485        
                InjectionMetadata.inject(Object, String, PropertyValues) line: 92          
                AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(PropertyValues, PropertyDescriptor[], Object, String) line: 284               
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).populateBean(String, AbstractBeanDefinition, BeanWrapper) line: 1106          
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 517          
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 456          
                AbstractBeanFactory$1.getObject() line: 294     
                DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 225         
                DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 291    
                DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 193     
                BeanDefinitionValueResolver.resolveReference(Object, RuntimeBeanReference) line: 323     
                BeanDefinitionValueResolver.resolveValueIfNecessary(Object, Object) line: 107          
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyPropertyValues(String, BeanDefinition, BeanWrapper, PropertyValues) line: 1360          
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).populateBean(String, AbstractBeanDefinition, BeanWrapper) line: 1118          
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 517          
                DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 456          
                AbstractBeanFactory$1.getObject() line: 294     
                DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 225         
                DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 291    
                DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 193     
                DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 284    
                DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 193     
                DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 284    
                DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 193     
                DefaultListableBeanFactory.preInstantiateSingletons() line: 587            
                XmlWebApplicationContext(AbstractApplicationContext).finishBeanFactoryInitialization(ConfigurableListableBeanFactory) line: 925              
                XmlWebApplicationContext(AbstractApplicationContext).refresh() line: 472   
                ContextLoaderListener(ContextLoader).configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext, ServletContext) line: 383            
                ContextLoaderListener(ContextLoader).initWebApplicationContext(ServletContext) line: 283
                ContextLoaderListener.contextInitialized(ServletContextEvent) line: 111          
                WebAppContext(ContextHandler).callContextInitialized(ServletContextListener, ServletContextEvent) line: 771               
                WebAppContext(ServletContextHandler).callContextInitialized(ServletContextListener, ServletContextEvent) line: 424              
                WebAppContext(ContextHandler).startContext() line: 763        
                WebAppContext(ServletContextHandler).startContext() line: 249         
                WebAppContext.startContext() line: 1250          
                WebAppContext(ContextHandler).doStart() line: 706   
                WebAppContext.doStart() line: 492       
                WebAppContext(AbstractLifeCycle).start() line: 64        
                Server(HandlerWrapper).doStart() line: 95        
                Server.doStart() line: 277            
                Server(AbstractLifeCycle).start() line: 64             
                MreJettyServer.start(int) line: 85            
                MreJettyServer.main(String[]) line: 59 

Affects: 3.1.3

Referenced from: commits cf290ab

1 votes, 4 watchers

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions