一直對Spring創建bean的順序很好奇,現在總算有時間寫個代碼測試一下。不想看過程的小伙伴可以直接看結論
目錄結構:
其中:bean4、bean5包下的class沒有注解@Component,測試過程中,這兩個包的class會直接通過<bean class="XXXXX"/>的方式創建。bean1、bean2、bean3包下的class注解了@Component,以便component-scan掃描。另外,bean創建之間沒有依賴關系,例如bean1的創建不依賴於其他bean。
applicationContext1.xml
<bean class="com.luych.test.springBeanCreateOrderTest.bean5.Bean5_2"/> <context:component-scan base-package="com.luych.test.springBeanCreateOrderTest.bean2" /> <bean class="com.luych.test.springBeanCreateOrderTest.bean5.Bean5_1"/>
applicationContext2.xml
<bean class="com.luych.test.springBeanCreateOrderTest.bean4.Bean4_1"/> <context:component-scan base-package="com.luych.test.springBeanCreateOrderTest.bean1" /> <bean class="com.luych.test.springBeanCreateOrderTest.bean4.Bean4_2"/>
springMVC-servlet.xml
<context:component-scan base-package="com.luych.test.springBeanCreateOrderTest.bean3" />
web.xml
<servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:springMVC-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:applicationContext*.xml </param-value> </context-param>
運行結果
class com.luych.test.springBeanCreateOrderTest.bean5.Bean5_2 has been created
class com.luych.test.springBeanCreateOrderTest.bean2.Bean2_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean2.Bean2_2 has been created
class com.luych.test.springBeanCreateOrderTest.bean5.Bean5_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean4.Bean4_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean1.Bean1_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean1.Bean1_2 has been created
class com.luych.test.springBeanCreateOrderTest.bean4.Bean4_2 has been created
class com.luych.test.springBeanCreateOrderTest.bean3.Bean3_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean3.Bean3_2 has been created
結論一:
1. 在web.xml中,ContextLoaderListener和DispatcherServlet的書寫順序不會影響相應的xml文件加載順序。ContextLoaderListener中的xml先加載,DispatcherServlet中的xml后加載。
2. ContextLoaderListener中如果contextConfigLocation通過模糊匹配到多個xml文件時,xml按照文件命名順序加載。但是如果contextConfigLocation逐個指定了具體加載某個xml,則會按照其指定順序加載。
本例中可以改為(注意下面代碼中藍色部分):
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:applicationContext2.xml,classpath*:applicationContext1.xml </param-value> </context-param>
則其加載順序為:
class com.luych.test.springBeanCreateOrderTest.bean4.Bean4_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean1.Bean1_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean1.Bean1_2 has been created
class com.luych.test.springBeanCreateOrderTest.bean4.Bean4_2 has been created
class com.luych.test.springBeanCreateOrderTest.bean5.Bean5_2 has been created
class com.luych.test.springBeanCreateOrderTest.bean2.Bean2_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean2.Bean2_2 has been created
class com.luych.test.springBeanCreateOrderTest.bean5.Bean5_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean3.Bean3_1 has been created
class com.luych.test.springBeanCreateOrderTest.bean3.Bean3_2 has been created
3. 同一個spring的xml文件中,bean的加載順序按照書寫順序加載
4. 通過component-scan掃描的方式加載bean,在掃描范圍內按照class的命名順序加載
以上的測試過程中,bean的創建是沒有依賴關系的,那么如果bean之間的創建存在依賴關系,則被依賴的bean會被優先創建。但是如果存在相互依賴的情況,則會發生異常。
例如:
@Component public class Bean2_1 { private final Bean2_2 bean2_2; @Autowired public Bean2_1(Bean2_2 bean2_2) { this.bean2_2 = bean2_2; System.out.println(Bean2_1.class + " has been created"); } }
@Component public class Bean2_2 { private final Bean2_1 bean2_1; @Autowired public Bean2_2(Bean2_1 bean2_1) { this.bean2_1 = bean2_1; System.out.println(Bean2_2.class + " has been created"); } }
異常信息:
Error creating bean with name 'bean2_1' defined in file......