Spring配置文件中直接定義bean時自動注入失敗研究

一個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

相關文章
相關標籤/搜索