<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 4.5 Bean 的范圍 當創建 bean 時,也就創建了對通過 bean 定義創建的類真正實例的配方。bean 定義的 配方這個概念是很重要的,因為它就意味著,你可以通過配方來創建一個類的多個對象實例。 你不僅可以控制從特定 bean 定義創建出的對象的各個依賴和要配置到對象中的值,也 可以控制對象的范圍。這個方法非常強大并且很靈活,你可以選擇通過配置創建的對象的范 圍,而不必去處理 Java 類級別對象的范圍。Bean 可以被定義部署成一種范圍:開箱,Spring Framework 支持五種范圍,里面的三種僅僅通過感知 web 的 ApplicationContext 可用。 下列的范圍支持開箱。你也可以創建自定義范圍(4.5.5 節)。 表 4.3 Bean 的范圍 | 范圍 | 描述 | | --- | --- | | 單例(4.5.1 節) | (默認的)為每一個 Spring 的 IoC 容器定義一個 bean 為獨立對象的實例。 | | 原型(4.5.2 節) | 定義可以有任意個對象實例的 bean。 | | 請求(4.5.4.2 節) | 定義生命周期為一個獨立 HTTP 請求的 bean;也就是說,每一個 HTTP 請求有一個 bean 的獨立的實例而不是一個獨立的 bean。僅僅在可感知 Web 的 Spring ApplicationContext 中可用。 | | 會話(4.5.4.3 節) | 定義一個生命周期為一個 HTTP Session 的獨立的 bean。僅僅在可感知 Web 的 Spring ApplicationContext 中可用。 | | 全局會話(4.5.4.4 節) | 定義一個生命周期為一個全局的 HTTP Session 的獨立的 bean。典型地是僅僅 使用 portlet 上 下文 時可 用。 僅僅 在可 感知 Web 的 Spring ApplicationContext 中可用。 | 線程范圍的 bean 在 Spring 3.0 當中,線程范圍是可用的,但是它默認是不注冊的。要獲取更多信息,請 參考 [SimpleThreadSc ope](http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/context/support/SimpleThreadScope.html) 的 JavaDoc 文檔。對于如何注冊這個范圍或其它自定義范圍的做法, 請參考 4.5.5.2 節,“使用自定義范圍”。 ### 4.5.1 單例范圍 僅僅共享被管理的 bean 的一個實例,所有使用一個或多個 id 來匹配 bean 的結果對 bean 請求,由 Spring 容器返回指定 bean 的實例。 另外一種方式,當你定義一個范圍是單例的 bean 后,Spring 的 IoC 容器通過 bean 的定 義創建了這個對象的一個實例。這個獨立的實例存儲在單例 bean 的緩存中,所有對這個命 名 bean 的后續的請求和引用都返回緩存的對象。 ![image](img/Image_114.jpg) Spring 中對單例 bean 的概念和四人幫(Gang of Four,GoF)設計模式書中定義的單例 模式是不同的。GoF 的單例硬編碼了對象的范圍,也就是特定的類僅僅能有一個實例被每一 個 ClassLoader 來創建。Spring 中單例 bean 的范圍,是對每一個容器和 bean 來說的。這 就意味著在獨立的 Spring 容器中,對一個特定的類創建了一個 bean,那么 Spring 容器通過 bean 的定義僅僅創建這個類的一個實例。在 Spring 中,單例范圍是默認的范圍。要在 XML 中定義單例的 bean,可以按照如下示例來編寫: ``` <bean id="accountService" class="com.foo.DefaultAccountService"/> <!-- 盡管是冗余的(單例范圍是默認的),下面的bean定義和它是等價的 --> <bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/> ``` ### 4.5.2 原型范圍 非單例,原型范圍的 bean 就是當每次對指定 bean 進行請求時,一個新的 bean 的實例 就會創建。也就是說,bean 被注入到其它 bean 或是在容器中通過 getBean()方法來請求時就 會創建新的 bean 實例。作為一項規則,對所有有狀態的 bean 使用原型范圍,而對于無狀 態的 bean 使用單例范圍。 下圖講述了 Spring 的原型范圍。數據訪問對象(DAO)通常不是配置成原型的,因為典型的 DAO 不會持有任何對話狀態;僅僅是對作者簡單對單例圖的重用。 ![image](img/Image_116.jpg) 下面的示例在 XML 中定義了原型范圍的 bean: ``` <!-- 使用spring-beans-2.0.dtd --> <bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/> ``` 和其它范圍相比,Spring 不管理原型 bean 的完整生命周期;容器實例化,配置并裝配 原型對象,并把他給客戶端,對于原型 bean 的實例來說,就沒有進一步的跟蹤記錄了。因 此,盡管初始化生命周期回調方法可以對所有對象調用而不管范圍,那么在原型范圍中,配 置銷毀生命周期回調是不能被調用的。客戶端代碼必須清理原型范圍的對象并且釋放原型 bean 持有的系統資源。要讓 Spring 容器來釋放原型范圍 bean 持有的資源,可以使用自定義 bean 后處理器(4.8.1 節),它持有需要被清理的 bean 的引用。 在某些方面,關于原型范圍 bean 的 Spring 容器的角色就是對 Java new 操作符的替代。 所有之后生命周期的管理必須由客戶端來處理。(關于 Spring 容器中 bean 的生命周期的詳細內容,可以參考 4.6.1 節,“生命周期回調”) ### 4.5.3 單例 bean 和原型 bean 依賴 當你使用單例范圍的 bean 和其依賴是原型范圍的 bean 時,要當心依賴實在實例化時 被解析的。因此,如果依賴注入了原型范圍的 bean 到單例范圍的 bean 中,新的原型 bean 就被實例化并且依賴注入到單例 bean 中。原型實例是唯一的實例并不斷提供給單例范圍的 bean。 假設你想單例范圍的 bean 在運行時可以重復獲得新的原型范圍 bean 的實例。那么就 不能將原型范圍的 bean 注入到單例 bea n 中,因為這個注入僅僅發生一次,就是在 Spring 容器實例化單例 bean 并解析和注入它的依賴時。如果你在運行時需要原型 bean 新的實例 而不是僅僅一次,請參考 4.4.6 節,“方法注入”。 ### 4.5.4 請求,會話和全局會話范圍 request,session 和 global session 范圍僅僅當你使用感知 Web 的 Spring ApplicationContext 實現類可用(比如 XmlWebApplicationContext)。如果你在 常規的 Spring IoC 容器中使用如 ClassPathXmlApplicationContext 這些范圍,那么 就會因為一個未知的 bean 的范圍而得到 IllegalStateException 異常。 #### 4.5.4.1 初始化 Web 配置 要支持 request,session 和 global session 級別(Web 范圍的 bean)范圍的 bean,一些細微的初始化配置就需要在定義 bean 之前來做。(這些初始化的設置對標準的 范圍,單例和原型,是不需要的。) 如果來完成這些初始化設置是基于你使用的特定 Servlet 容器的。 如果使用 Spring 的 Web MVC 來訪問這些范圍的 bean,實際上,是由 Spring 的 DispatcherServlet 或 DispatcherPortlet 來處理請求的,那就沒有特殊的設置了: DispatcherServlet 和 DispatcherPortlet 已經可以訪問所有相關的狀態。 如果你使用支持 Servlet 2.4 規范以上的 Web 容器,在 Spring 的 DispatcherServle(比如,當 使 用 JSF 或 Struts 時 ) 之 外 來 處 理 請 求 , 那 么 就 需 要 添 加 javax.servlet.ServletRequestListener 到 Web 應用的 web.xml 文件的聲明中: ``` <web-app> ... <listener> <listener-class> org.springframework.web.context.request.RequestContextLi stener </listener-class> </listener> ... </web-app> ``` 如果你使用老版的 Web 容器( Serlvet 2.3 ), 那 么 就 使 用 提 供 的 javax.servlet.Filter 實 現 類 。 如 果 你 想 在 Servlet 2.3 容 器 中 , 在 Spring 的 DispatcherServlet 外部的請求中訪問 Web 范圍的 bean,下面的 XML 配置代碼片段就必須包 含在 Web 應用程序的 web.xml 文件中。(過濾器映射基于所處的 Web 應用程序配置,所以 你必須以適當的形式來修改它。) ``` <web-app> .. <filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestCont extFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ... </web-app> ``` DispatcherServlet,RequestContextListener 和 RequestContextFilter 所有都可以做相同的事情,即綁定 HTTP 請求對象到服務于請求的 Thread 中。這使得 bean 在請求和會話范圍內對后續的調用鏈可用。 #### 4.5.4.2 請求范圍 考慮一下下面的 bean 定義: ``` <bean id="loginAction" class="com.foo.LoginAction" scope="request"/> ``` Spring 容器通過使用 LoginAction bean 的定義來為每個 HTTP 請求創建 LoginAction bean 的新的實例。也就是說,LoginAction bean 是在 HTTP 請求級別的范 圍。你可以改變創建的實例內部的狀態,怎么改都可以,因為從相同的 LoginAction bean 定義中創建的其它實例在狀態上不會看到這些改變;它們對單獨的請求都是獨立的。當請求 完成處理,bean 在請求范圍內被丟棄了。 #### 4.5.4.3 會話范圍 考慮一下下面的 bean 定義: ``` <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> ``` Spring 容器通過使用 UserPreferences bean 的定義來為每個 HTTP 會話的生命周期 創建 UserPreferences bean 的新的實例。換句話說,UserPreferences bean 在 HTTP session 級別的范圍內是有效的。正如 request-scoped 的 bean,你可以改變創建的實 例 內 部 的 狀 態 ,怎 么 改 都 可以 , 要 知 道其 它 HTTP session 實例也是使用從相同 UserPreferences bean 的定義中創建的實例,它們不會看到狀態的改變,因為它們對單 獨的 HTTP session 都是獨立的。當 HTTP session 最終被丟棄時,那么和這個 HTTP session 相關的 bean 也就被丟棄了。 #### 4.5.4.4 全局會話范圍 考慮一下下面的 bean 定義: ``` <bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/> ``` global session 范圍和標準的 HTTP Session 范圍(上面 4.5.4.3 節討論的)類似, 但僅能應用于基于 portlet 上下文的 Web 應用程序。portlet 規范定義了全局的 Session 的 概念,該會話在所有 portlet 之間所共享來構建一個單獨的 portlet Web 應用程序。在 global session 范圍內定義的 bean 的范圍(約束)就會在全局 portlet Session 的生命周期內。 如果你編寫了一個標準的基于 Servlet 的 Web 應用,并且定義了一個或多個有 global session 范圍的 bean,那么就會使用標準的 HTTP Session 范圍,也不會拋出什么錯誤。 #### 4.5.4.5 各種范圍的 bean 作為依賴 Spring 的 IoC 容器不僅僅管理對象(bean)的實例,而且也裝配協作者(依賴)。如果 你想注入(比如)一個 HTTP 請求范圍的 bean 到另外一個 bean 中,那么就必須在有范圍 bean 中注入一個 AOP 代理。也就是說,你需要注入一個代理對象來公開相同的公共接口作為有 范圍的對象,這個對象也可以從相關的范圍(比如,HTTP 請求范圍)中獲取真實的目標對 象并且委托方法調用到真正的對象上。 > ![](img/note.gif) > 注意 > 你不需要使用`<aop:scoped-proxy/>`來結合 singleton 和 prototype 范圍的 bean 。 如 果 你 想 為 單 例 bean 嘗 試 創 建 有 范 圍 的 代 理 , 那 么 就 會 拋 出 BeanCreationException 異常。 在下面的示例中,配置僅僅只有一行,但是這對理解“為什么”和其中的“怎么做”是至關重要的。 ``` <?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring -beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring -aop-3.0.xsd"> <!-- HTTP 會話范圍的bean作為代理 --> <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> <!-- 這個元素影響周圍bean的代理 --> <aop:scoped-proxy/> </bean> <!-- 單例范圍的bean用上面的代理bean來注入 --> <bean id="userService" class="com.foo.SimpleUserService"> <!-- 作為代理的userPreferences bean引用 --> <property name="userPreferences" ref="userPreferences"/> </bean> </beans> ``` 要創建這樣的代理,就要插入一個子元素`<aop:scoped-proxy/>`到有范圍的 bean 的 定義中。(如果要選擇基于類的代理,那么需要在類路徑下放置 CGLIB 的類庫,參考下面的 “選擇創建代理類型”部分和“附錄 C,基于 XML Schema 的配置”)為什么 request, session,globalSession 和自定義范圍級別的 bean 需要`<aop:scoped-proxy/>`元 素?我們來看看下面的單例 bean 定義,并將它和你需要定義的上述范圍來比較一下。(下面 的 userPreferences bean 的定義是不完整的。) ``` <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/> <bean id="userManager" class="com.foo.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean> ``` 在上面 的例 子中 ,單 例 bean userManager 是用 HTTP session 范圍 的 bean userPreferences 的引用注入的。這里突出的一點就是 userManager bean 是單例的; 那么對于每個容器來說,它只會被實例化一次,并且它的依賴(本例中只有一個依賴,就是 userPreferences bean)也只會被注入一次。這就意味著 userManager bean 只會對同 一個 userPreferences 對象進行操作,也就是說,是最初被注入的那一個對象。 這并不是你想要的行為,即注入生命周期范圍短的 bea n 到生命周期范圍長的 bea n 中, 比如注入 HTTP session 范圍的協作 bean 作為依賴到單例 bean 中。相反,你需要一個單獨的 userManager 對 象 , 生 命 周 期 和 HTTP session 一 樣 , 你 需 要 一 個 userPreferences 對象來特定于 HTTP session。因此容器創建對象,公開相同的公共 接口,而 UserPreferences 類(理想情況下是 UserPreferences 實例的對象)從范圍機制(HTTP 請求,session 等等)來獲取真正的 UserPreferences 對象。容器注入這 個代理對象到 userManager bean 中,這個 bean 不能感知 UserPreferences 的引用就 是代理。在這個示例中,當 UserManager 實例調用依賴注入的 UserPreferences 對象 的方法時,那么其實是在代理上調用這個方法。然后代理從(在本例中)HTTP session 中 獲取真實的 UserPreferences 對象 ,并且委托方法調用到獲取的真實 的 UserPreferences 對象中。 因此,當注入 request 范圍,session 范圍和 globalSession 范圍的 bean 到協作 對象時,你需要下面的,正確的,完整的配置: ``` <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"> <aop:scoped-proxy/> </bean> <bean id="userManager" class="com.foo.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean> ``` 選擇創建代理類型 默認情況下,當 Spring 容器為被`<aop:scoped-proxy/>`元素標記的 bean 創建代理時, 基于 CGLIB 的類的代理就被創建了。這就是說在應用程序的類路徑下需要有 CGLIB 的類庫。 注意:CGLIB 代理僅僅攔截公有的方法調用!不會調用代理中的非公有方法;它們不會被委托到有范圍的目標對象。 另外,你可以配置 Spring 容器有范圍的 bean 創建標準的基于 JDK 接口的代理,通過指 定`<aop:scoped-proxy/>`元素的 proxy-target-class 屬性值為 false。使用基于 JDK 接口的代理意味著你不需要在應用程序的類路徑下添加額外的類庫來使代理生效。然而, 它也意味著有范圍 bean 的類必須實現至少一個接口,并且注入到有范圍 bean 的所有協作 者必須通過它的一個接口來引用 bean。 ``` <!—DefaultUserPreferences實現了UserPreferences接口 --> <bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session"> <aop:scoped-proxy proxy-target-class="false"/> </bean> <bean id="userManager" class="com.foo.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean> ``` 關于選擇基于類或基于接口的代理的更多詳細信息,可以參考 8.6 節,“代理機制”。 ### 4.5.5 自定義范圍 在 Spring 2.0 當中,bean 的范圍機制是可擴展的。你可以定義自己的范圍,或者重新定 義已有 的范 圍, 盡管 后者 被認 為是 不良 實踐 而且 你不能 覆蓋 內建 的 singleton 和 prototype 范圍。 #### 4.5.5.1 創建自定義范圍 要 整 合 自 定 義 范 圍 到 Spring 的 容 器 中 , 那 么 需 要 實 現 org.springframework.beans.factory.config.Scope 接口,這會在本節中來介紹。 對于如何實現你自己的范圍的想法,可以參考 Spring Framework 本身提供的 Scope 實現和 [Scope](http://static.springframework.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/config/Scope.html) 的 JavaDoc文檔,這里會介紹你需要實現的方法的更多細節。 Scope 接口有四個方法來從作用域范圍中獲取對象,從作用域范圍中移除對象還有允 許它們被銷毀。 下面的方法從底層范圍中返回對象。比如,會話范圍的實現,返回會話范圍的 bean(如 果它不存在,在綁定到會話后為將來的引用,這個方法返回 bean 的新的實例)。 ``` Object get(String name, ObjectFactory objectFactory) ``` 下面的方法從底層范圍中移除對象。比如會話范圍的實現,從底層的會話中移除會話范 圍的 bean。對象應該被返回,但是如果特定名字的對象沒有被發現的話可以返回 null。 ``` Object remove(String name) ``` 下面的方法注冊當被銷毀或者指定范圍內的對象被銷毀時,相關范圍應用執行的回調函 數。參考 JavaDoc 文檔或者 Spring 范圍實現來獲取更多關于銷毀回調的信息。 ``` void registerDestructionCallback(String name, Runnable destructionCallback) ``` 下面的方法得到一個底層范圍的會話標識符。這個標識符對每種范圍都是不同的。對于 會話范圍的實現,這個標識符可以是會話標識符。 ``` String getConversationId() ``` #### 4.5.5.2 使用自定義范圍 在你編寫并測試一個或多個自定義 Scope 實現之后,你需要讓 Spring 容器感知你的新 的范圍。下面的方法是用 Spring 容器注冊新的 Scope 的核心方法。 ``` void registerScope(String scopeName, Scope scope); ``` 這個方法在 ConfigurableBeanFactory 接口中聲明,也在 大多數的 ApplicationContext 實現類中可用,通過 BeanFactory 屬性跟著 Spring。 registerScope(..)方法的第一個參數是和范圍相關的唯一名稱;在 Spring 容器本 身這樣的名字就是 singleton 和 prototype。registerScope(..)方法的第二個參數 是你想注冊并使用的自定義 Scope 實現的真實實例。 假設你編寫了自定義的 Scope 實現,之后便如下注冊了它。 > ![](img/note.gif) > 注意 > 下面的示例使用了 SimpleThreadScope,這是包含在 Spring 當中的,但模式是沒有 注冊的。這些指令和你自己自定義的 Scope 的實現是相同的。 ``` Scope threadScope = new SimpleThreadScope(); beanFactory.registerScope("thread", threadScope); ``` 之后就可以為你的自定義 Scope 創建符合 Spring 規則的 bean 的定義: ``` <bean id="..." class="..." scope="thread"> ``` 使用自定義的 Scope 實現,并沒有被限制于程序注冊。你也可以進行聲明式的 Scope注冊,使用 CustomScopeConfigurer 類: ``` <?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring -beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring -aop-3.0.xsd"> <bean class="org.springframework.beans.factory.config.CustomScopeC onfigurer"> <property name="scopes"> <map> <entry key="thread"> <bean class="org.springframework.context.support.SimpleT hreadScope"/> </entry> </map> </property> </bean> <bean id="bar" class="x.y.Bar" scope="thread"> <property name="name" value="Rick"/> <aop:scoped-proxy/> </bean> <bean id="foo" class="x.y.Foo"> <property name="bar" ref="bar"/> </bean> </beans> ``` > ![](img/note.gif) > 注意 > 當在 FactoryBean 的實現中放置`<aop:scoped-proxy/>`時,那就是工廠 bean 本身被放 置到范圍中,而不是從 getObject()方法返回的對象。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看