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

jontejj picture jontejj · May 15, 2013 · Viewed 11.2k times · Source

And if so which configuration is needed? Is this not recommended?

The 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;
    }
}

The dependency bean that fails:

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();
    }
}

A custom BeanFactoryPostProcessor with the "faulty" 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 BeanFactoryPostProcessorConfiguration reference DependantBean?

The resulting DependantBean instance in AppTest is not null, i.e it's created by spring, but its dependencies (DependencyBean) are null. The fact that Spring doesn't complain at all leads me to believe that this is a bug within spring. Should this use-case be supported or not?

Btw, I'm using spring-*-3.1.1.RELEASE.jar Btw 2: the code to reproduce the bug can also be found here.

Answer

Pavel Horal picture Pavel Horal · May 28, 2013

Maybe more simpler and descriptive answer:

Yes, it is possible to use @Component bean as BeanFactoryPostProcessor dependency.

However every dependency of a BeanFactoryPostProcessor will be instantiated before any BeanPostProcessor is active. And these include:

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

So tu sum it up:

Yes, it is possible to use @Component bean as BeanFactoryPostProcessor dependency, but they can not use annotation based injection (@Autowired, @Resource, @WebServiceRef, ...) and other features provided by BeanPostProcessors .


Workaround for your example might be to create ApplicationContext hierarchy as you have suggested:

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

Other approaches might be (which I would prefer):

  • Use BeanFactoryAware interface on your @Component bean and pull your dependency yourself (as Spring will not inject it).
  • Define beans connected with BeanFactoryPostProcessors within context configuration XML or @Configuration (i.e. don't use @Component for these beans).