Is it possible to insert a bean argument defined with @Component as an argument to BeanFactoryPostProcessor?

And if what configuration is needed? Is this not recommended?

Annotated class:

package com.springbug.beanfactorydependencyissue; import javax.annotation.Resource; import org.springframework.stereotype.Component; @Component public class DependantBean { @Resource DependencyBean dependencyBean; // Isn't initialized correctly public DependencyBean getDependencyBean() { return dependencyBean; } } 

Bean dependency that is not running:

 package com.springbug.beanfactorydependencyissue; import org.springframework.stereotype.Component; @Component public class DependencyBean { } 

TestCase:

 package com.springbug.beanfactorydependencyissue; import static org.fest.assertions.Assertions.assertThat; import javax.annotation.Resource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; import org.testng.annotations.Test; import com.springbug.beanfactorydependencyissue.DependantBean; @ContextConfiguration(locations = "/applicationContext.xml") public class AppTest extends AbstractTestNGSpringContextTests { @Resource private DependantBean annotatedBean; @Test public void testThatDependencyIsInjected() { // Fails as dependency injection of annotatedBean.dependencyBean does not work assertThat(annotatedBean.getDependencyBean()).isNotNull(); } } 

Custom BeanFactoryPostProcessor with an "erroneous" dependency:

 package com.springbug.beanfactorydependencyissue; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class BeanFactoryPostProcessorConfiguration { /** * The {@link DependantBean} here causes the bug, can * {@link BeanFactoryPostProcessor} have regular beans as dependencies? */ @Bean public static BeanFactoryPostProcessor beanFactoryPostProcessor( DependantBean dependantBean) { return new BeanFactoryPostProcessor() { public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory) throws BeansException { } }; } } 

applicationContext.xml:

 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.springbug.beanfactorydependencyissue" /> </beans> 

Why can't a BeanFactoryPostProcessorConfiguration reference DependantBean ?

The resulting instance of DependantBean in AppTest not null, i.e. it was created by spring, but its dependencies ( DependencyBean ) are zero. The fact that Spring doesn't complain at all makes me believe this is a bug in spring. Should this precedent be maintained or not?

Btw, I am using spring - * - 3.1.1.RELEASE.jar Btw 2: the code for reproducing the error can also be found here .

+7
source share
4 answers

Thanks to some serious spring debugging, we found that the DependantBean parameter to BeanFactoryPostProcessorConfiguration caused the impatient initialization of the other (unrelated) beans. But since spring was at the BeanFactoryPostProcessor stage, BeanPostProcessors were not ready.

Reading the javadoc for BeanFactoryPostProcessor (thanks @Pavel for pointing this out) exactly explains the problem:

BeanFactoryPostProcessor can interact and define bean definitions, but never bean instances. This can lead to premature bean creation, container malfunction, and unintended side effects. If interaction with a bean instance is required, consider implementing {@link BeanPostProcessor}.

Decision:

Slightly modified applicationContext.xml :

 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.stackoverflow.springbug.beanfactorydependencyissue.other" /> </beans> 

New bootstrapContext.xml : (note that only packages are different)

 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.stackoverflow.springbug.beanfactorydependencyissue.bootstrap" /> </beans> 

New Contexts.java : (Note that bootstrap is the parent context for the regular applicationContext)

 package com.stackoverflow.springbug.beanfactorydependencyissue; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; public final class Contexts { private static Supplier<ApplicationContext> bootstrap = Suppliers.memoize(new Supplier<ApplicationContext>(){ public ApplicationContext get() { return new ClassPathXmlApplicationContext("/bootstrapContext.xml"); } }); /** * Context for beans that are needed before initializing of other beans. */ public static ApplicationContext bootstrap() { return bootstrap.get(); } private static Supplier<ApplicationContext> applicationContext = Suppliers.memoize(new Supplier<ApplicationContext>(){ public ApplicationContext get() { return new ClassPathXmlApplicationContext(new String[]{"/applicationContext.xml"}, bootstrap()); } }); public static ApplicationContext applicationContext() { return applicationContext.get(); } } 

BeanFactoryPostProcessorConfiguration without DependantBean as a parameter:

 package com.stackoverflow.springbug.beanfactorydependencyissue.other; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.stackoverflow.springbug.beanfactorydependencyissue.Contexts; import com.stackoverflow.springbug.beanfactorydependencyissue.bootstrap.DependantBean; @Configuration public class BeanFactoryPostProcessorConfiguration { /** * The {@link DependantBean} here caused the bug, {@link Contexts#bootstrap()} is used as a * workaround. */ @Bean public static BeanFactoryPostProcessor beanFactoryPostProcessor() { final DependantBean dependantBean = Contexts.bootstrap().getBean(DependantBean.class); System.out.println(dependantBean.getDependencyBean()); return new BeanFactoryPostProcessor(){ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } }; } } 

The last thing to do is move the DependantBean and DependencyBean to the bootstrap package. The goal was reached to read @Value properties from the database. Reusing old beans definitions and not duplicating beans.

+3
source

Perhaps a simpler and more descriptive answer:

Yes, you can use the @Component bean as a BeanFactoryPostProcessor dependency.

However, any dependency of a BeanFactoryPostProcessor will be created before any BeanPostProcessor is active. These include:

  • CommonAnnotationBeanPostProcessor - responsible for @PostConstruct , @Resource and some other annotations
  • AutowiredAnnotationBeanPostProcessor - responsible for @Autowired and @Value annotations
  • ... and much more...

So, summarize:

Yes, you can use the @Component bean as a BeanFactoryPostProcessor dependency, but they cannot use annotation-based injection ( @Autowired , @Resource , @WebServiceRef , ...) and other functions provided by BeanPostProcessor .


A workaround for your example could be to create an ApplicationContext hierarchy, as you suggested:

  • Each context initializes and applies its own mail processor infrastructure, where you can still reference dependencies on parent contexts.

Other approaches might be (which I would prefer):

  • Use the BeanFactoryAware interface on your @Component bean and pull your dependency yourself (since Spring will not inject it).
  • Define the beans associated with the BeanFactoryPostProcessor in the XML context configuration or @Configuration (i.e. do not use @Component for these beans).
+5
source

You need to specify the identifier of your component, like this

  @Component("myClass") public class MyClass implements MyInterface { @Resource private MyDependency myDependency; //Isn't initialized correctly when listOfMyClassBeans references myClass //Implementation skipped for brevity sake... } 

and then use the link

  <ref bean="myClass"> 
0
source

Try using the Spring Util namespace and specify the type of value. Refer to this question

0
source

All Articles