Spring框架下類的初始化順序


 序言

  之前的已經分析過在不使用框架的情況下,類中各個部分的初始化或執行順序,后來我在開發中使用了Spring,發現初始化順序與之前的稍有不同,特別是其初始化以xml配置文檔作為驅動,xml中先定義生么類就試圖優先實例化這個類,搞得我有點糾結。現在來細細測試研究一下。

  這次采用的測試代碼與之前的類似:有三個主線類B、C和D,其中D繼承C,C繼承B,這三個類中均包含static塊、普通初始化塊和無參的構造方法;有兩個輔助類E和F,B中包含E類和F類的成員變量,F類成員變量是static類型,E類的成員變量是普通類型;程序運行入口在A.java中。為了符合Spring的開發思路,增加了兩個接口IE和IF,E和F分別實現這兩個接口。

 正文

  IE.java

1 package chloe.spring;
2 
3 public interface IE 
4 {
5     public void funcOfE();
6 }

  IF.java

1 package chloe.spring;
2 
3 public interface IF 
4 {
5     public void funcOfF();
6 }

  E.java

 1 package chloe.spring;
 2 
 3 public class E implements IE
 4 {
 5     E()
 6     {
 7         System.out.println("執行E的構造函數");
 8     }
 9     public void funcOfE()
10     {
11         System.out.println("執行E的函數");
12     }
13 }

  F.java

 1 package chloe.spring;
 2 
 3 public class F implements IF
 4 {
 5     F()
 6     {
 7         System.out.println("執行F的構造函數");
 8     }
 9     public void funcOfF()
10     {
11         System.out.println("執行F的函數");
12     }
13 }

  B.java

 1 package chloe.spring;
 2 
 3 public class B 
 4 {
 5     protected IE e;//實現的類和實例由Spring容器注入
 6     protected static IF f;//f的實現的類和實例由Spring容器注入
 7     protected String sb;//初始值由Spring容器依據配置文件給出
 8     static
 9     {
10         System.out.println("執行B類的static塊(B包含E類的成員變量,包含靜態F類成員變量)");
11         //f.funcOfF();屬性f的注入在構造函數之后,更在此static初始化之后,所以這里不能調用f的函數,調用的話會報初始化異常
12     }
13     {
14         System.out.println("執行B實例的普通初始化塊");
15     }
16     B()
17     {
18         System.out.println("執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)");
19         //e.funcOfE();屬性e的注入在此構造函數之后,所以這里不能調用f的函數,調用的話會報初始化異常
20     }
21     
22     public String getSb()
23     {
24         return sb;
25     }
26     public void setSb(String sb1)
27     {
28         this.sb=sb1;
29         System.out.println("已給B的成員變量sb賦初值:"+sb1);
30     }
31     
32     public void setE(IE e1)
33     {    
34         this.e = e1;    
35         System.out.println("已將E類實例注入B的引用成員變量e");
36     }
37     
38     public void setF(IF f1)//此方法不能加static修飾符,否則Spring注入時報NotWritablePropertyException
39     {    
40         f = f1;    
41         System.out.println("已將F類實例注入B的static引用成員變量f");
42     }
43 }

  C.java

 1 package chloe.spring;
 2 
 3 public class C extends B
 4 {
 5     static
 6     {
 7         System.out.println("執行C的static塊(C繼承B)");
 8     }
 9     {
10         System.out.println("執行C的普通初始化塊");
11     }
12     C()
13     {
14         System.out.println("執行C的構造函數(C繼承B)");
15     }
16 }

  D.java

 1 package chloe.spring;
 2 
 3 public class D extends C
 4 {
 5     
 6     protected static String sd;//由Spring容器依據配置文件賦初始值
 7     
 8     static
 9     {
10         System.out.println("執行D的static塊(D繼承C)");
11         
12     }
13     {
14         System.out.println("執行D實例的普通初始化塊");
15     }
16     protected String sd1;//由Spring容器依據配置文件賦初始值
17     D()
18     {
19         System.out.println("執行D的構造函數(D繼承C);父類B的實例成員變量sb的值為:"+sb+";本類D的static成員變量sd的值為:"+sd+";本類D的實例成員變量sd1的值是:"+sd1);
20     }
21     
22     public void methodOfD()
23     {
24         System.out.println("運行D中的方法methodOfD");
25     }
26     
27     public void setSd(String sdtmp)
28     {
29         sd=sdtmp;
30         System.out.println("已初始化D的static成員變量sd");
31     }
32     
33     public void setSd1(String sd1tmp)
34     {
35         sd1=sd1tmp;
36         System.out.println("已初始化D的實例成員變量sd1");
37         
38     }
39 }

A.java

 1 package chloe.spring;
 2 
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 
 6 public class A 
 7 {
 8     public static void main(String[] args)
 9     {
10         System.out.println("====運行A中的main函數,准備載入xml配置文件====");
11         ApplicationContext appContext=new ClassPathXmlApplicationContext("applicationContext1.xml");
12         System.out.println("====xml配置文件載入完畢,准備獲得bean D====");
13         D d=(D)appContext.getBean("beand");
14         System.out.println("====已經獲取bean D,准備運行D中的方法methodOfD====");
15         d.methodOfD();
16         
17     }
18 }

  配置文件applicationContext1.xml內容:

 1 <?xml version="1.0" encoding="UTF-8"?><!--Spring框架需要使用的bean定義文件-->
 2 <beans
 3     xmlns="http://www.springframework.org/schema/beans"
 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5     xmlns:p="http://www.springframework.org/schema/p"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 7 
 8     <bean id="beane" class="chloe.spring.E"></bean>
 9 
10     <bean id="beanf" class="chloe.spring.F"></bean>
11 
12     <bean id ="beanb" class="chloe.spring.B">
13         <property name="e" ref="beane"></property>  <!--設置bean的成員變量的實現類-->
14         <property name="f" ref="beanf"></property>
15         <property name="sb" value="初始sb"></property>
16     </bean>
17 
18     <bean id ="beanc" class="chloe.spring.C"></bean>
19 
20     <bean id="beand" class="chloe.spring.D">    
21         <property name="sd1" value="初始sd1"></property>
22         <property name="sd" value="初始sd"></property>    
23     </bean>
24 
25 </beans>

  運行A.java后輸出如下結果:

 1 ====運行A中的main函數,准備載入xml配置文件====
 2 2012-10-28 19:06:40 org.springframework.context.support.AbstractApplicationContext prepareRefresh
 3 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@a01335: startup date [Sun Oct 28 19:06:40 CST 2012]; root of context hierarchy
 4 2012-10-28 19:06:40 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
 5 信息: Loading XML bean definitions from class path resource [applicationContext1.xml]
 6 2012-10-28 19:06:40 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
 7 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1fc2fb: defining beans [beane,beanf,beanb,beanc,beand]; root of factory hierarchy
 8 執行E的構造函數
 9 執行F的構造函數
10 執行B類的static塊(B包含E類的成員變量,包含靜態F類成員變量)
11 執行B實例的普通初始化塊
12 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
13 已將E類實例注入B的引用成員變量e
14 已將F類實例注入B的static引用成員變量f
15 已給B的成員變量sb賦初值:初始sb
16 執行C的static塊(C繼承B)
17 執行B實例的普通初始化塊
18 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
19 執行C的普通初始化塊
20 執行C的構造函數(C繼承B)
21 執行D的static塊(D繼承C)
22 執行B實例的普通初始化塊
23 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
24 執行C的普通初始化塊
25 執行C的構造函數(C繼承B)
26 執行D實例的普通初始化塊
27 執行D的構造函數(D繼承C);父類B的實例成員變量sb的值為:null;本類D的static成員變量sd的值為:null;本類D的實例成員變量sd1的值是:null
28 已初始化D的實例成員變量sd1
29 已初始化D的static成員變量sd
30 ====xml配置文件載入完畢,准備獲得bean D====
31 ====已經獲取bean D,准備運行D中的方法methodOfD====
32 運行D中的方法methodOfD

  由輸出結果可知,在main函數中執行ApplicationContext appContext=new ClassPathXmlApplicationContext("applicationContext1.xml")時,Spring便開始了對類和實例的初始化,初始化順序與xml配置文件中bean的定義順序一致,且一個類的構造函數運行時機總是早於其成員變量的初始化時機,不管成員變量是否是static類型,這一點與不用Spring框架的中的順序不同。此時如果把xml文件中B的定義的放在E的前面,則在執行完B的構造函數后,注入其屬性之前實例化E和F,這時的輸出結果為:

 1 ====運行A中的main函數,准備載入xml配置文件====
 2 2012-10-28 20:06:56 org.springframework.context.support.AbstractApplicationContext prepareRefresh
 3 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@a01335: startup date [Sun Oct 28 20:06:56 CST 2012]; root of context hierarchy
 4 2012-10-28 20:06:56 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
 5 信息: Loading XML bean definitions from class path resource [applicationContext1.xml]
 6 2012-10-28 20:06:56 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
 7 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1fc2fb: defining beans [beanb,beane,beanf,beanc,beand]; root of factory hierarchy
 8 執行B類的static塊(B包含E類的成員變量,包含靜態F類成員變量)
 9 執行B實例的普通初始化塊
10 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
11 執行E的構造函數
12 執行F的構造函數
13 已將E類實例注入B的引用成員變量e
14 已將F類實例注入B的static引用成員變量f
15 已給B的成員變量sb賦初值:初始sb
16 執行C的static塊(C繼承B)
17 執行B實例的普通初始化塊
18 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
19 執行C的普通初始化塊
20 執行C的構造函數(C繼承B)
21 執行D的static塊(D繼承C)
22 執行B實例的普通初始化塊
23 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
24 執行C的普通初始化塊
25 執行C的構造函數(C繼承B)
26 執行D實例的普通初始化塊
27 執行D的構造函數(D繼承C);父類B的實例成員變量sb的值為:null;本類D的static成員變量sd的值為:null;本類D的實例成員變量sd1的值是:null
28 已初始化D的實例成員變量sd1
29 已初始化D的static成員變量sd
30 ====xml配置文件載入完畢,准備獲得bean D====
31 ====已經獲取bean D,准備運行D中的方法methodOfD====
32 運行D中的方法methodOfD

  我們來繼續分析更改xml配置文件前的輸出結果,即第一個輸出結果。由第10到12行以及第13到15行可知,無繼承關系的類B的初始化順序為:static初始化塊 --> 普通初始化塊 --> 構造函數 --> 成員變量實例化和初始化 。另外由第16到20行以及第21到27行可知,如果某個類繼承了父類,而父類static塊之前已執行,則初始化順序為:子類static初始化塊-->父類普通初始化塊-->父類構造函數,如果父類還有父類,則先運行父類的父類的初始化塊,這是一個遞歸的過程。

  如果初始化子類時,父類的還未初始化,即父類的static塊還沒有執行過,那順序應該是怎樣的呢?我們將xml配置文件中D類的定義移到B和C的定義之前,再運行,得到如下輸出:

 1 ====運行A中的main函數,准備載入xml配置文件====
 2 2012-10-28 20:55:39 org.springframework.context.support.AbstractApplicationContext prepareRefresh
 3 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@a01335: startup date [Sun Oct 28 20:55:39 CST 2012]; root of context hierarchy
 4 2012-10-28 20:55:40 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
 5 信息: Loading XML bean definitions from class path resource [applicationContext1.xml]
 6 2012-10-28 20:55:40 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
 7 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1fc2fb: defining beans [beane,beanf,beand,beanb,beanc]; root of factory hierarchy
 8 執行E的構造函數
 9 執行F的構造函數
10 執行B類的static塊(B包含E類的成員變量,包含靜態F類成員變量)
11 執行C的static塊(C繼承B)
12 執行D的static塊(D繼承C)
13 執行B實例的普通初始化塊
14 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
15 執行C的普通初始化塊
16 執行C的構造函數(C繼承B)
17 執行D實例的普通初始化塊
18 執行D的構造函數(D繼承C);父類B的實例成員變量sb的值為:null;本類D的static成員變量sd的值為:null;本類D的實例成員變量sd1的值是:null
19 已初始化D的實例成員變量sd1
20 已初始化D的static成員變量sd
21 執行B實例的普通初始化塊
22 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
23 已將E類實例注入B的引用成員變量e
24 已將F類實例注入B的static引用成員變量f
25 已給B的成員變量sb賦初值:初始sb
26 執行B實例的普通初始化塊
27 執行B類的構造函數(B包含E類的成員變量,包含靜態F類成員變量)
28 執行C的普通初始化塊
29 執行C的構造函數(C繼承B)
30 ====xml配置文件載入完畢,准備獲得bean D====
31 ====已經獲取bean D,准備運行D中的方法methodOfD====
32 運行D中的方法methodOfD

  可以看出,實例化E和F之后,准備初始化D,而此時D的父類C還未初始化和實例化(被Spring容器載入),於是要先初始化C,同樣,C的父類B也沒有初始化,於是先初始化B,這樣就出現了第10到12行的結果。另外static初始化塊的執行要早於普通初始化塊和構造函數,因此執行完D的static塊后才執行其父類的普通初始化塊和構造函數。另外還可以看出,如果父類之前已經實例化(執行過普通初始化塊了構造函數),之后又有該父類的子類需要初始化,則該父類的普通初始化塊和構造函數又要執行一遍,但該父類的static塊不會再執行,我想這是由於父類的構造函數也是子類構造函數的一部分,只是在子類的構造函數中省略了,實際創建子類的實例時,也要運行父類的構造函數才能完整地實例化子類。

  由輸出結果的最后三行可知,運行D d=(D)appContext.getBean("beand")時,並不會再次初始化和實例化D,好像只是將變量d指向了Spring容器中已經存在的D實例。所以我推測,使用Spring時,在配置文件中定義的類都是在運行new ClassPathXmlApplicationContext("applicationContext1.xml")時一起初始化和實例化的,並且完成了實例之間的連接,之后getBean只是引用這些已經存在的實例,這樣就比較節約內存。因為傳統的new方法每調用一次就會在內存中新建一個實例,這樣同一類型的實例會有很多個副本。

 總結

  在Spring框架下,類的初始化順序優先級如下:

   1. 按照xml配置文件中類的定義順序加載類並創建類的實例。

  2. 假設當前要加載X類,則先運行X的static塊。如果此時X的父類Y還沒有加載,則先查找配置文件來加載Y,運行Y的static塊,這樣一直遞歸下去。當X的所有先驅類的static塊運行完畢后,再類似遞歸地實例化X的先驅類(運行先驅類的普通初始化塊和構造函數),完成所有先驅類的實例化后才運行X的普通初始化塊和構造函數。

  3. 執行完X的構造函數之后,開始根據xml配置文件給X的成員變量賦初值。如果X的引用型成員變量所屬的類還未加載,則先查找配置文件來加載(初始化、實例化)該成員變量所屬的類,加載完畢后將其注入給X的成員變量,完成成員變量的初始化。

  在前一篇文章(http://www.cnblogs.com/zhouqing/archive/2012/10/26/2741916.html)中,如果在定義成員變量的同時對該成員變量實例化,則可以在構造函數中調用成員變量實例的函數,但是使用了Spring后,如果成員變量交給Spring容器實例化,則在構造函數中不能調用該成員變量的實例方法,因為在構造函數完成之前,成員變量還沒有實例化,結果就會報空指針異常。在使用Spring時,到底將哪些類交給Spring容器管理,哪些類在代碼中管理,不能一概而論,要根據功能需要來考慮。另外如果多次運行new ClassPathXmlApplicationContext("applicationContext1.xml"),則會再次重新載入和實例化一遍,這樣很木有必要,而且容易導致數據不一致,因此一般將new ClassPathXmlApplicationContext的結果作為某個類的static全局變量,隨時被其他的類使用,而包含這個全局變量的類就不要托管給Spring了喲~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM