Annotation-drived auto injection dose not work for bean instantiation if the bean implements BeanFactoryPostProcessor
interface. Both ClassPathXmlApplication
and AnnotationConfApplicationContext
met this problem, I think wether this should be spring's bug or not. Here are tow examples helping to reproduce:
By XML
package autoinject;
public class BusinessService {
}
package autoinject;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class SampleBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Autowired
private BusinessService businessService;
public BusinessService getBusinessService() {
return businessService;
}
public void setBusinessService(BusinessService businessService) {
this.businessService = businessService;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.sogou.bizdev.iflow" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean class="autoinject.SampleBeanFactoryPostProcessor" id="sampleBeanFactoryPostProcessor"/>
<bean class="autoinject.BusinessService" id="businessService"/>
</beans>
package autoinject;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AutoInjectTest {
@Test
public void testAutoInject() {
ConfigurableApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:autoinject.xml");
SampleBeanFactoryPostProcessor beanFactoryPostProcessor = (SampleBeanFactoryPostProcessor) appContext.getBean("sampleBeanFactoryPostProcessor");
Assert.assertNotNull("NOT INJECTED!!!", beanFactoryPostProcessor.getBusinessService());
}
}
Output:
......
java.lang.AssertionError: NOT INJECTED!!!
at org.junit.Assert.fail(Assert.java:89)
at org.junit.Assert.assertTrue(Assert.java:42)
......
Property businessService
is not auto injected for bean sampleBeanFactoryPostProcessor
.
By Annotation
package annotationautoinject;
import org.springframework.stereotype.Component;
@Component
public class BookDao {
}
package annotationautoinject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BusinessService {
@Autowired
private BookDao bookDao;
public BookDao getBookDao() {
return bookDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
package annotationautoinject;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class SampleBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Autowired
private BusinessService businessService;
public BusinessService getBusinessService() {
return businessService;
}
public void setBusinessService(BusinessService businessService) {
this.businessService = businessService;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
package annotationautoinject;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackageClasses = Main.class)
public class Main {
}
package annotationautoinject;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AnnotationAutoInjectTest {
@Test
public void testAutoInject() {
ConfigurableApplicationContext appContext = new AnnotationConfigApplicationContext(Main.class);
BusinessService businessService = (BusinessService) appContext.getBean("businessService");
Assert.assertNotNull("businessService IS NULL!!!", businessService);
Assert.assertNotNull("bookDao IS NOT INJECTED!!!", businessService.getBookDao());
SampleBeanFactoryPostProcessor beanFactoryPostProcessor = (SampleBeanFactoryPostProcessor) appContext.getBean("sampleBeanFactoryPostProcessor");
Assert.assertNotNull("sampleBeanFactoryPostProcessor IS NULL!!!", beanFactoryPostProcessor);
Assert.assertNotNull("businessService IS NOT INJECTED!!!", beanFactoryPostProcessor.getBusinessService());
}
}
Output:
......
java.lang.AssertionError: businessService IS NOT INJECTED!!!
at org.junit.Assert.fail(Assert.java:89)
at org.junit.Assert.assertTrue(Assert.java:42)
......
Property businessService
is not auto injected for bean sampleBeanFactoryPostProcessor
, but property bookDao
is injected for bean businessService
.
Tested spring version: 2.5.5
, 3.1.0
, 4.3.10
, 5.2.8
Please help to confirm this problem, thanks.
Comment From: jhoeller
This is by design: BeanFactoryPostProcessor
is a very low-level callback and comes before any annotation processing (which is implemented in subsequent BeanPostProcessor
callbacks). Either wire up your post-processor explicitly (e.g. using <property>
) or use programmatic BeanFactory.getBean
calls within the post-processor implementation. Alternatively, consider putting your business-dependent logic into some other location; BeanFactoryPostProcessor
is really meant to be an infrastructure callback.
FWIW, a corresponding note can be found in our core beans chapter in the reference documentation.
Comment From: liuming-dev
Thanks for your reply @jhoeller, I'm trying to find other way to do this.