一個Spring注入問題,首先看一個普通Spring Bean,java
public class Foo { @Autowired Bar bar; public void doSomething(){ bar.doSomething(); } }
Spring配置一:spring
<bean id="bar" class="com.test.Bar"></bean> <bean id="foo" class="com.test.Foo"></bean>
單元測試:app
@Test public void test_doSomthing(){ ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-test.xml"); Foo foo = ctx.getBean(Foo.class); foo.doSomething(); }
執行上述測試方法,報錯單元測試
java.lang.NullPointerException at com.test.Foo.doSomething(Foo.java:15) at com.test.FooTest.test_doSomthing(FooTest.java:13)
即foo bean中的bar並未注入。測試
Spring配置二:ui
<context:component-scan base-package="com.test"></context:component-scan>
當改爲配置二後執行上述單元測試方法便能成功經過。經分析日誌及查看源代碼,發現使用配置二時供裝載了6個bean,以下所示:spa
DEBUG org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216) Loaded 6 bean definitions from location pattern [applicationContext-test.xml] DEBUG org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:530) Bean factory for org.springframework.context.support.ClassPathXmlApplicationContext@3c4e80d3: org.springframework.beans.factory.support.DefaultListableBeanFactory@14cc51c8: defining beans [bar,foo,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor]; root of factory hierarchy
而使用配置一時只有兩個bean,以下所示:調試
DEBUG org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216) Loaded 2 bean definitions from location pattern [applicationContext-test.xml] DEBUG org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:530) Bean factory for org.springframework.context.support.ClassPathXmlApplicationContext@45ebbb93: org.springframework.beans.factory.support.DefaultListableBeanFactory@18481697: defining beans [bar,foo]; root of factory hierarchy
配置二執行單元測試經過的緣由彷佛就在於多出的這幾個bean。是否是隻要有context:component-scan元素在自動就會有這幾個bean的產生?驗證此假設日誌
在配置一中添加一個無實際意義的context:component-scan元素,以下所示:code
<context:component-scan base-package="com.nonexist"></context:component-scan>
這時執行單元測試能經過,同配置二同樣也會裝載6個bean。那麼這6個bean中到底哪一個對注入bar到Foo中起了做用呢?
通過斷點調試發現是AutowiredAnnotationBeanPostProcessor bean起了做用,見輸出日誌:
2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.annotation.InjectionMetadata.<init>(InjectionMetadata.java:60) Found injected element on class [com.test.Foo]: AutowiredFieldElement for com.test.Bar com.test.Foo.bar 2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:85) Processing injected method of bean 'foo': AutowiredFieldElement for com.test.Bar com.test.Foo.bar 2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:245) Returning cached instance of singleton bean 'bar' 2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.registerDependentBeans(AutowiredAnnotationBeanPostProcessor.java:424) Autowiring by type from bean name 'foo' to bean named 'bar' 2015-04-25 20:23:09 DEBUG org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) Finished creating instance of bean 'foo'
那麼直接在配置一種顯式添加AutowiredAnnotationBeanPostProcessor bean呢?以下所示:
<bean id="bar" class="com.tcl.account.service.test.Bar"></bean> <bean id="foo" class="com.tcl.account.service.test.Foo"></bean> <bean id="autowiredAnnotationBeanPostProcessor" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"></bean>
測試會不會經過?會經過。見日誌:
DEBUG org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216) Loaded 3 bean definitions from location pattern [applicationContext-test.xml] DEBUG org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:530) Bean factory for org.springframework.context.support.ClassPathXmlApplicationContext@7767d3c1: org.springframework.beans.factory.support.DefaultListableBeanFactory@1924ed52: defining beans [bar,foo,autowiredAnnotationBeanPostProcessor]; root of factory hierarchy DEBUG org.springframework.beans.factory.annotation.InjectionMetadata.<init>(InjectionMetadata.java:60) Found injected element on class [com.test.Foo]: AutowiredFieldElement for com.test.Bar com.test.Foo.bar DEBUG org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:85) Processing injected method of bean 'foo': AutowiredFieldElement for com.test.Bar com.test.Foo.bar DEBUG org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:245) Returning cached instance of singleton bean 'bar' DEBUG org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.registerDependentBeans(AutowiredAnnotationBeanPostProcessor.java:424) Autowiring by type from bean name 'foo' to bean named 'bar' DEBUG org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) Finished creating instance of bean 'foo'
那麼爲何在配置文件中添加了context: componet-scan元素後就會自動添加那另外4個bean呢?通過斷點調試發現Spring隱式裝載的4個bean是在以下方法中加載的:
Set<BeanDefinitionHolder> org.springframework.context.annotation.AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source)
其調用鏈以下所示:
補充一:
若仍用配置一,但單元測試改爲以下形式也能夠測試經過。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:/applicationContext-test.xml" }) public class FooTest2 { @Autowired private Foo foo; @Test public void test_doSomthing(){ foo.doSomething(); } }
固然一點都不意外,這種方式也會隱式加載那4個bean,見日誌:
DEBUG org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:216) Loaded 2 bean definitions from location pattern [classpath:/applicationContext-test.xml] INFO org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:500) Refreshing org.springframework.context.support.GenericApplicationContext@51f3336e: startup date [Sun Apr 26 17:27:35 CST 2015]; root of context hierarchy DEBUG org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:530) Bean factory for org.springframework.context.support.GenericApplicationContext@51f3336e: org.springframework.beans.factory.support.DefaultListableBeanFactory@4f9d1352: defining beans [bar,foo,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor]; root of factory hierarchy
補充二,若使用的是@Value,效果同@Autowired,即也須要隱式加載AutowiredAnnotationBeanPostProcessor bean。
public class Foo { @Value("${bar}") String bar; public void doSomething(){ System.out.println(bar); } }
補充三:
若使用配置一,@PostConstruct標註的方法也不會被執行,但此時須要隱式加載的Spring bean是:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
補充四:
在配置一中添加以下配置
<context:annotation-config/>
也會隱式加載那4個bean