1. 程式人生 > >Spring學習(十五)Spring Bean 的5種作用域介紹

Spring學習(十五)Spring Bean 的5種作用域介紹

Spring Bean 中所說的作用域,在配置檔案中即是“scope 在面向物件程式設計中作用域一般指物件或變數之間的可見範圍。 而在Spring容器中是指其建立的Bean物件相對於其他Bean物件的請求可見範圍 在Spring 容器當中,一共提供了5種作用域型別,在配置檔案中,通過屬性scope來設定bean的作用域範圍。 1.    singleton:
<bean id="userInfo" class="cn.lovepi.UserInfo" scope="singleton"></bean>
當Bean的作用域為singleton的時候,Spring容器中只會存在一個共享的Bean例項,所有對Bean的請求只要id與bean的定義相匹配,則只會返回bean的同一例項。單一例項會被儲存在單例快取中,為Spring的預設作用域。
2.    prototype:
<bean id="userInfo" class="cn.lovepi.UserInfo" scope=" prototype "></bean>
每次對該Bean請求的時候,Spring IoC都會建立一個新的作用域。 對於有狀態的Bean應該使用prototype,對於無狀態的Bean則使用singleton 3.    request:
<bean id="userInfo" class="cn.lovepi.UserInfo" scope=" request "></bean>
Request作用域針對的是每次的Http請求,Spring容器會根據相關的Bean的
定義來建立一個全新的Bean例項。而且該Bean只在當前request內是有效的。 4.    session:
<bean id="userInfo" class="cn.lovepi.UserInfo" scope=" session "></bean>
針對http session起作用,Spring容器會根據該Bean的定義來建立一個全新的Bean的例項。而且該Bean只在當前http session內是有效的。 5.    global session:
<bean id="userInfo" class="cn.lovepi.UserInfo"scope=“globalSession"></bean>
類似標準的http session作用域,不過僅僅在基於portlet的web應用當中才有意義。Portlet規範定義了全域性的Session的概念。他被所有構成某個portlet外部應用中的各種不同的portlet所共享。在global session作用域中所定義的bean被限定於全域性的portlet session的生命週期範圍之內。 解析來我們詳細的介紹每種作用域的功能: 1)singleton作用域 是指在Spring IoC容器中僅存在一個Bean的示例,Bean以單例項的方式存在,單例項模式是重要的設計模式之一,在Spring中對此實現了超越,可以對那些非執行緒安全的物件採用單例項模式。 接下來看一個示例:
1. <bean id="car" class="cn.lovepi.Car" scope="singleton"></bean>
2. <bean id="boss1" class="cn.lovepi .Boss” p:car-ref=“car"></bean>
3. <bean id="boss2" class="cn.lovepi .Boss” p:car-ref=“car"></bean>
4. <bean id="boss3" class="cn.lovepi .Boss” p:car-ref=“car"></bean>
在1中car這個Bean生命週期宣告為了singleton模式,其他的bean如2,3,4引用了這個Bean。在容器中boss1、boss2、boss3的屬性都指向同一個bean car。如下圖所示:

不僅在容器中通過物件引入配置注入引用相同的car bean,通過容器的getBean()方法返回的例項也指向同一個bean。 在預設的情況下Spring的ApplicationContext容器在啟動的時候,自動例項化所有的singleton的bean並緩存於容器當中。 雖然啟動時會花費一定的時間,但是他卻帶來了兩個好處:
  1. 對bean提前的例項化操作,會及早發現一些潛在的配置的問題。
  2. Bean以快取的方式執行,當執行到需要使用該bean的時候,就不需要再去例項化了。加快了執行效率。
2)prototype作用域 是指每次從容器中呼叫Bean時,都返回一個新的例項,即每次呼叫getBean()時,相當於執行new Bean()的操作。在預設情況下,Spring容器在啟動時不例項化prototype的Bean。 接下來看一個示例:
1. <bean id=“car" class="cn.lovepi.Car" scope=“prototype"></bean>
2. <bean id=“boss1" class="cn.lovepi.Boss” p:car-ref=“car"></bean>
3. <bean id=“boss2" class="cn.lovepi.Boss” p:car-ref=“car"></bean>
4. <bean id=“boss3" class="cn.lovepi.Boss” p:car-ref=“car"></bean>
通過以上的配置,Boss1、boss2、boss3所引用的都是一個新的car的例項,每次通過容器getBean()方法返回的也是一個新的car例項。如下圖所示:
在預設情況下,Spring容器在啟動時不例項化prototype這種bean。此外,Spring容器將prototype的例項交給呼叫者之後便不在管理他的生命週期了。 3)當用戶使用Spring的WebApplicationContext時,還可以使用另外3種Bean的作用域,即request,sessionglobalSession 在使用Web應用環境相關的Bean作用域時,必須在Web容器中進行一些額外的配置: 對於低版本Web容器配置: 使用者可以使用http過濾器來進行配置並在url-pattern中對所有的頁面進行過濾。
<filter>
    <filter-name>requestContextFilter</filter-name>
    <filter-class>org.springframework.web.filter.RequestContextFilter</filterclass>
</filter>
<filter-mapping>
    <filter-name>requestContextFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
對於高版本Web容器配置: 使用http請求監聽器來進行配置。
<listener> <listener-class>
    org.springframework.web.context.request.RequestContextListener
</listener-class></listener>
ServletContextListener只負責監聽web容器啟動和關閉的事件, RequestContextListener實現了ServletRequestListener監聽器介面,該監聽器監聽http請求事件。 Web伺服器接收到的每一次請求都會通知該監聽器。 Spring容器的啟動和關閉事件由web容器的啟動和關閉事件來觸發。 但如果Spring容器中的bean需要requestsessionglobalSession作用域的支援,Spring容器本身就必須獲得web容器的http請求事件。以http請求事件來驅動bean的作用域的控制邏輯,也就是說通過配置RequestContextListener介面,Spring和web容器的結合就更緊密了。 那麼接下來簡單的介紹下這三種作用域。 1.request作用域        對應一個http請求和生命週期,當http請求呼叫作用域為request的bean的時候,Spring便會建立一個新的bean,在請求處理完成之後便及時銷燬這個bean。 2.session作用域        Session中所有http請求共享同一個請求的bean例項。Session結束後就銷燬bean。 3.globalSession作用域        與session大體相同,但僅在portlet應用中使用。 Portlet規範定義了全域性session的概念。請求的bean被組成所有portlet的自portlet所共享。 如果不是在portlet這種應用下,globalSession則等價於session作用域 接下來使用一個例項來演示下bean作用域的概念: 首先我們建立scope包,然後在包中建立兩個Java Bean:老闆(Boss.java)以及轎車(Car.java)
public class Boss {
    private String name;
    private Car car;

    public Boss() {
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Boss{" +
                "name='" + name + '\'' +
                ", car=" + car +
                '}';
    }
}

public class Car {
    private String color;
    private String brand;
    private double price;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

}
接著我們建立對應的建立配置檔案,在conf包中建立一個conf-scope.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"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="car" class="cn.lovepi.chapter03.scope.Car" scope="singleton"/>
  <bean id="boss1" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />
  <bean id="boss2" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />
  <bean id="boss3" class="cn.lovepi.chapter03.scope.Boss" p:car-ref="car" />
</beans>
最後我們建立測試類進行測試
public class Main {
    public static void main(String[] args){
        //載入配置檔案,啟動IoC容器
        BeanFactory factory=
                new FileSystemXmlApplicationContext("src/conf/conf-scope.xml");
        //獲得bean例項
        Boss boss1=factory.getBean("boss1",Boss.class);
        Boss boss2=factory.getBean("boss2",Boss.class);
        Boss boss3=factory.getBean("boss3",Boss.class);
        System.out.println(boss1.getCar());
        System.out.println(boss2.getCar());
        System.out.println(boss3.getCar());
    }
}
然後將作用域設定成singleton和prototype分別執行程式觀察結果。 接下來再來簡單的學習下在Spring當中如何自定義作用域 在Spring 2.0中,Spring的Bean作用域機制是可以擴充套件的,這意味著,你不僅可以使用Spring提供的預定義Bean作用域,還可以定義自己的作用域,甚至重新定義現有的作用域(不提倡這麼做,而且你不能覆蓋內建的singleton和prototype作用域) 1.首先需要實現自定義Scope類:       org.springframework.beans.factory.config.Scope       首先我們得實現config.scope這個介面,要將自定義scope整合到Spring容器當中就必須要實現這個介面。介面中只有兩個方法,分別用於底層儲存機制獲取和刪除這個物件。 2.在實現一個或多個自定義Scope並測試通過之後,接下來便是如何讓Spring容器來識別新的作用域。我們需要註冊自定義Scope類,使用registerScope方法      ConfigurableBeanFactory.registerScope(String scopeName, Scope scope) 其中:第一個引數是與作用域相關的全域性唯一的名稱,第二個引數是準備實現的作用域的例項。 3.在實現和註冊自定義的scope類之後我們便來使用自定義的Scope:
Scope customScope = new ThreadScope();
     beanFactory.registerScope(“thread”, customeScope);
     <bean id=“***” class=“***”  scope=“scopeName” />