天天看點

Spring-管理bean作用域、生命周期

一、管理bean作用域

在前面,将bean交給了Spring容器管理,在用戶端隻要調用getBean方法就可以從容器裡擷取bean執行個體,大家想想,每調用getBean方法,那麼它從容器裡擷取的bean到底是同一個呢?還是不同?   怎麼判斷是否是同一個對象呢?可以這樣做,代碼如下: 

SpringTest.java 

Java代碼 

package junit.test;   

import org.junit.BeforeClass;   

import org.junit.Test;   

import org.springframework.context.ApplicationContext;   

import org.springframework.context.support.ClassPathXmlApplicationContext;   

import cn.itcast.service.PersonService;   

public class SpringTest {   

    @BeforeClass  

    public static void setUpBeforeClass() throws Exception {   

    }   

    @Test  

    public void instanceSpring() {   

        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");   

        PersonService personService1 = (PersonService) ctx.getBean("personService");   

        PersonService personService2 = (PersonService) ctx.getBean("personService");   

        System.out.println(personService1 == personService2);   

        // 怎麼判斷兩個對象擷取的是否是同一個? 很簡單,比較引用是否相同就可以了   

    }   

package junit.test;

import org.junit.BeforeClass;

import org.junit.Test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.itcast.service.PersonService;

public class SpringTest {

 @BeforeClass

 public static void setUpBeforeClass() throws Exception {

 }

 @Test

 public void instanceSpring() {

  ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

  PersonService personService1 = (PersonService) ctx.getBean("personService");

  PersonService personService2 = (PersonService) ctx.getBean("personService");

  System.out.println(personService1 == personService2);

  // 怎麼判斷兩個對象擷取的是否是同一個? 很簡單,比較引用是否相同就可以了

 }

}

beans.xml 

Xml代碼 

<?xml version="1.0" encoding="UTF-8"?>  

<beans xmlns="http://www.springframework.org/schema/beans"  

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

       xsi:schemaLocation="http://www.springframework.org/schema/beans   

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  

          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"></bean>  

</beans> 

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"></bean>

</beans>如果傳回為true,代表他們引用的對象都是同一個;如果傳回的是false,那麼得到的就是兩個不同的對象。 

運作單元測試代碼,輸出結果為 true。 說明personService1和personService2這兩個變量所引用的對象都是同一個,這就證明了:在預設情況下,bean交給Spring容器管理後,那麼這個bean是一個單執行個體。 

   那麼我們能不能改變這種行為呢?假如現在我不想用單執行個體,我希望每調用一次getBean方法都擷取一個新的執行個體,那該怎麼做呢?  這就牽涉到Bean的作用域了

Bean的作用域 

---------------------------------------------------------------- 

.singleton 

在每個Spring IoC容器中一個bean定義隻有一個對象執行個體。預設情況下會在容器啟動時初始化bean,但我們可以指定Bean節點的lazy-init=“true”來延遲初始化bean,這時候,隻有第一次擷取bean會才初始化bean。如: 

<bean id="xxx" class="cn.itcast.OrderServiceBean" lazy-init="true"/> 

如果想對所有bean都應用延遲初始化,可以在根節點beans設定default-lazy-init=“true“,如下: 

<beans default-lazy-init="true“ ...>

prototype    每次從容器擷取bean都是新的對象。 

request 

session 

global session

.request.session .global session 這三種是web應用才能用的。這裡不是web應用,是以不能用。 

上面那個單例模式指定的就是singleton作用範圍。  如果沒有指定作用域的話,那麼預設就是singleton作用域範圍。 

當業務需求時每調用一次getBean方法,都要擷取一個新的執行個體的話,這個時候就可以通過prototype這個屬性值來指定。這個屬性值代表:每調用一下getBean方法,都能從容器裡擷取到新的執行個體

SpringTest.java不變。 

beans.xml如下: 

Xml代碼 

<?xml version="1.0" encoding="UTF-8"?>  

<beans xmlns="http://www.springframework.org/schema/beans"  

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

       xsi:schemaLocation="http://www.springframework.org/schema/beans   

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">  

          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"></bean>  

</beans> 

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"></bean>

</beans>運作單元測試代碼,得出的是false,代表每調用一下getBean方法,它都會傳回一個新的對象

二、管理bean生命周期

前面介紹了bean的作用域,這裡就引用出了一些問題,大家想想,這bean什麼時候進行執行個體化呢? 

  先改為單執行個體模式,來看看什麼時候執行個體化,beans.xml如下:

Xml代碼 

<?xml version="1.0" encoding="UTF-8"?>   

<beans xmlns="http://www.springframework.org/schema/beans"   

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   

       xsi:schemaLocation="http://www.springframework.org/schema/beans    

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">   

          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">                  

          </bean>   

</beans>  

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"> </bean> </beans>究竟是調用getBean方法時進行執行個體化?還是Spring容器起動時就進行執行個體化呢?下面就進行一下驗證,打開PersonServiceBean.java,我們驗證什麼時候執行個體化,有個最簡單的方法,在它的預設構造函數裡面列印出一句話, 

PersonServiceBean.java

Java代碼 

package cn.itcast.service.impl;    

import cn.itcast.service.PersonService;    

public class PersonServiceBean implements PersonService {    

    public PersonServiceBean(){    

        System.out.println("我被執行個體化了。。");    

    }    

    public void save(){    

        System.out.println("我是save()方法");    

    }    

}  

package cn.itcast.service.impl; import cn.itcast.service.PersonService; public class PersonServiceBean implements PersonService { public PersonServiceBean(){ System.out.println("我被執行個體化了。。"); } public void save(){ System.out.println("我是save()方法"); } }看看在預設的情況下,到底是調用getBean的時候輸出這句話?還是在執行個體化容器的時候? 

SpringTest.java

Java代碼 

package junit.test;    

import org.junit.BeforeClass;    

import org.junit.Test;    

import org.springframework.context.support.AbstractApplicationContext;    

import org.springframework.context.support.ClassPathXmlApplicationContext;    

public class SpringTest {    

    @BeforeClass   

    public static void setUpBeforeClass() throws Exception {    

    }    

    @Test public void instanceSpring(){    

        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");    

    }    

}  

package junit.test; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringTest { @BeforeClass public static void setUpBeforeClass() throws Exception { } @Test public void instanceSpring(){ ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); } }執行這個單元測試,從控制台可以看到輸出了“我被執行個體化了”這句話,由此可見,這個執行個體化對于這種預設情況下(它的作用範圍是單例的,而且并沒有修改任何的屬性),是在容器執行個體化的時候,就會對bean進行執行個體化。 

如果bean的作用域範圍是scope="prototype"的話,這個bean的執行個體化到底是在什麼時候呢?是在容器執行個體化之後執行個體化?還是在調用getBean方法後執行個體化呢? 

beans.xml

Xml代碼 

<?xml version="1.0" encoding="UTF-8"?>   

<beans xmlns="http://www.springframework.org/schema/beans"   

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   

       xsi:schemaLocation="http://www.springframework.org/schema/beans    

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">   

          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype">                

          </bean>   

</beans>  

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" scope="prototype"> </bean> </beans>

SpringTest.java

Java代碼 

package junit.test;    

import org.junit.BeforeClass;    

import org.junit.Test;    

import org.springframework.context.support.AbstractApplicationContext;    

import org.springframework.context.support.ClassPathXmlApplicationContext;    

import cn.itcast.service.PersonService;    

public class SpringTest {    

    @BeforeClass   

    public static void setUpBeforeClass() throws Exception {    

    }    

    @Test public void instanceSpring(){    

        System.out.println("11111111111");    

        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");    

        System.out.println("22222222222222222");    

        PersonService personService1 = (PersonService)ctx.getBean("personService");    

        System.out.println("333333333333333333333");    

        ctx.close();    

    }    

}  

package junit.test; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.itcast.service.PersonService; public class SpringTest { @BeforeClass public static void setUpBeforeClass() throws Exception { } @Test public void instanceSpring(){ System.out.println("11111111111"); ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); System.out.println("22222222222222222"); PersonService personService1 = (PersonService)ctx.getBean("personService"); System.out.println("333333333333333333333"); ctx.close(); } }輸出結果是: 

11111111111 

22222222222222222 

我被執行個體化了。。 

333333333333333333333 

這說明,當bean的作用域範圍是prototpye的時候,它是在我們調用getBean方法時才進行執行個體化的。那麼我們有沒有辦法去更改這種行為呢?   下面是單執行個體模式下,更改行為的代碼 

beans.xml

Xml代碼 

<?xml version="1.0" encoding="UTF-8"?>   

<beans xmlns="http://www.springframework.org/schema/beans"   

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   

       xsi:schemaLocation="http://www.springframework.org/schema/beans    

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">   

          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="true">   

          </bean>   

</beans>  

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="true"> </bean> </beans>lazy-init是延遲初始化屬性,假如設為false,就是不進行延遲初始化,那麼它就在容器執行個體化的時候對bean進行執行個體化,如果設成true的話,代表要進行延遲初始化,那麼Spring容器執行個體化的時候不會對bean進行執行個體化. 

運作單元測試,輸出結果為: 

11111111111 

22222222222222222 

我被執行個體化了。。 

333333333333333333333 

可以看出lazy-init這個屬性是起到了作用,我們告訴它不要在Spring容器執行個體化的時候,對PersonServiceBean這個bean進行執行個體化,通過lazy-init這個屬性就可以指定這種行為。 

如果想為配置檔案下所有的bean指定延遲初始化的話,可以在<beans>這個配置節點裡面指定屬性default-lazy-init="true".當然在實際運用中,是不建議大家使用這個屬性的,因為我們希望在應用起動的時候我們看下控制台列印出來的資訊,看一些bean是否可以完成它的執行個體化,如果你在調用getBean的時候才執行個體化這個bean,那就是說盡能在運作期才能發現錯誤,是以建議大家少用lazy-init這個屬性,除非你要完成一些特别的工作。

現在這個bean的建立時機我們已經知道了,那麼在有一些應用場合下,當它在建立bean的時候,我要執行一些資源的打開,也就是說是初始化操作(好比打開資料庫連接配接等等的操作),這時候我們該怎麼辦呢? 

PersonServiceBean.java如下,

Java代碼 

package cn.itcast.service.impl;    

import cn.itcast.service.PersonService;    

public class PersonServiceBean implements PersonService {    

    public void init(){    

        System.out.println("初始化");    

    }    

    public PersonServiceBean(){    

        System.out.println("我被執行個體化了。。");    

    }    

    public void save(){    

        System.out.println("我是save()方法");    

    }    

    public void destory(){    

        System.out.println("關閉打開的資源");    

    }    

}  

package cn.itcast.service.impl; import cn.itcast.service.PersonService; public class PersonServiceBean implements PersonService { public void init(){ System.out.println("初始化"); } public PersonServiceBean(){ System.out.println("我被執行個體化了。。"); } public void save(){ System.out.println("我是save()方法"); } public void destory(){ System.out.println("關閉打開的資源"); } }

很明顯,這個init方法,如果我們不做任何指定的話,它是不會執行的,Spring容器給我們提供了一個功能,可以指定我們的初始化方法(用init-method這個屬性),也就是說當PersonServiceBean這個bean被執行個體化之後,接下來就會執行init-method指定的方法。 

當然在實際應用中,除了初始化方法對一些資源的打開,我們還需要對資源進行釋放,也就是說我們要關閉一些資源,這時候呢,我們需要在PersonServiceBean這個bean被銷毀之前先執行destory方法,怎麼辦呢?我們可以指定destory-method屬性. 

大家想想PersonServiceBean這個bean到底什麼時候被銷毀的呢?如果你沒去人為的操作它的話(沒有人為的把它删掉),那麼預設情況下它是一直在Spring容器中的,也就是說随着Spring容器關閉了,它才會被銷毀, 

beans.xml

Xml代碼 

<?xml version="1.0" encoding="UTF-8"?>   

<beans xmlns="http://www.springframework.org/schema/beans"   

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   

       xsi:schemaLocation="http://www.springframework.org/schema/beans    

           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">   

          <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="false"   

            init-method="init" destory-method="destory">   

          </bean>   

</beans>  

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" lazy-init="false" init-method="init" destory-method="destory"> </bean> </beans>

SpringTest.java

Java代碼 

package junit.test;    

import org.junit.BeforeClass;    

import org.junit.Test;    

import org.springframework.context.ApplicationContext;    

import org.springframework.context.support.AbstractApplicationContext;    

import org.springframework.context.support.ClassPathXmlApplicationContext;    

import cn.itcast.service.PersonService;    

public class SpringTest {    

    @BeforeClass   

    public static void setUpBeforeClass() throws Exception {    

    }    

    @Test public void instanceSpring(){    

        System.out.println("11111111111");    

        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");    

        //用到了AbstractApplicationContext這個抽象類,這個類是被ClassPathXmlApplicationContext所繼承的    

        //是以我們可以通過AbstractApplicationContext來引用Spring容器的執行個體    

        System.out.println("22222222222222222");    

        PersonService personService1 = (PersonService)ctx.getBean("personService");    

        System.out.println("333333333333333333333");    

        ctx.close();//調用Spring容器裡面的close方法,對容器進行正常的關閉    

        //以前并沒有正常關閉Spring容器,因為我們的應用一執行完,這個Spring容器就被銷毀了    

        //以前的執行方法并不是正常的關閉Spring容器,close是正常的關閉容器    

    }    

}  

package junit.test; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.itcast.service.PersonService; public class SpringTest { @BeforeClass public static void setUpBeforeClass() throws Exception { } @Test public void instanceSpring(){ System.out.println("11111111111"); AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); //用到了AbstractApplicationContext這個抽象類,這個類是被ClassPathXmlApplicationContext所繼承的 //是以我們可以通過AbstractApplicationContext來引用Spring容器的執行個體 System.out.println("22222222222222222"); PersonService personService1 = (PersonService)ctx.getBean("personService"); System.out.println("333333333333333333333"); ctx.close();//調用Spring容器裡面的close方法,對容器進行正常的關閉 //以前并沒有正常關閉Spring容器,因為我們的應用一執行完,這個Spring容器就被銷毀了 //以前的執行方法并不是正常的關閉Spring容器,close是正常的關閉容器 } }運作,得到輸出結果: 

11111111111 

我被執行個體化了。。 

初始化 

22222222222222222 

333333333333333333333 

關閉打開的資源 

首先,先執行執行個體化,執行個體化之後呢再調用init方法,大家一定要注意它的執行時機(先執行個體化,再執行init方法).這個方法是容器通過反射技術來調用的。 

destory方法也已經被執行了

指定Bean的初始化方法和銷毀方法 

---------------------------------------------------------- 

指定Bean的初始化方法和銷毀方法 

<bean id="xxx" class="cn.itcast.OrderServiceBean" init-method="init" destroy-method="close"/>