一、管理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"/>