昨天把四種做用域作了說明,今天對於擴展使用進行深刻講解,今天的內容相對來講難,仔細體會其中的奧妙。javascript
做用域bean做爲依賴項 java
Spring IoC容器不只管理對象(bean)的實例化,還管理依賴注入。例如將一個HTTP request 做用域bean注入到另外一個更持久做用域的bean中,你能夠選擇注入一個AOP proxy 來代替做用域bean。就是說須要注入一個代理對象,該對象公開與做用域對象相同的public接口,但也能夠從相關做用域(如HTTP request)檢索真正的目標對象,並將方法調用委託給真正的對象。spring
還能夠在做用域爲singleton的bean之間使用<aop:scoped-proxy/>,而後經過一個可序列化的中間代理,從而可以在反序列化時從新獲取目標singleton bean。微信
當依靠做用域bean 爲 prototype聲明<aop:scoped-proxy/>時,共享代理上的每一個方法調用都會致使建立一個新的目標實例,而後將調用轉發到該實例。session
另外,scoped 代理並非生命週期中短暫訪問bean的惟一方法。還能夠將注入點(即構造方法或setter參數或autowired字段)聲明爲ObjectFactory<MyTargetBean>,容許getObject()調用在每次須要時按需獲取當前實例,而無需保留實例或單獨存儲實例。maven
做爲擴展屬性,能夠聲明ObjectProvider<MyTargetBean>,它提供了幾個額外的訪問屬性,包括getIfAvailable和getIfUnique。ide
它在JSR-330中稱爲Provider,用於Provider<MyTargetBean>聲明並每次經過相應get()調用。spa
使用JSR-330 標準註解:
.net
從Spring 3.0開始,Spring提供了對JSR-330標準註解(依賴注入)的支持。這些註解的掃描方式與Spring註解相同。要使用它們,須要在類路徑中包含相關的jar。prototype
若是使用Maven,javax.inject工件能夠在標準Maven存儲庫(https://repo1.Maven.org/maven2/javax/inject/javax.inject/1/)中找到。能夠將如下依賴項添加到文件pom.xml中:
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version></dependency>
如下示例中的配置僅爲一行,瞭解其背後的「why」和「how」很重要:
<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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 一個HTTP Session-scoped bean 做爲一個代理暴露在外 --> <bean id="userPreferences" class="com.something.UserPreferences" scope="session"> <!-- 告知或提示容器 代理的bean --> <aop:scoped-proxy/> </bean>
<!-- 一個singleton-scoped的bean被注入到上面bean的代理中--> <bean id="userService" class="com.something.SimpleUserService"> <!-- userPreferences 代理被引用 --> <property name="userPreferences" ref="userPreferences"/> </bean></beans>
要建立這樣的代理,能夠將子<aop:scoped-proxy/>嵌入到scoped bean中(請參見選擇要建立的代理類型和基於XML模式的配置)。爲何在request
, session
和自定義做用域級別定義做用域的bean須要<aop:scoped-proxy/>元素?考慮下面的singleton bean定義,並將其與你須要爲上述做用域定義的內容進行對比(注意下面的userPreferences
bean是不完整的):
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
<bean id="userManager" class="com.something.UserManager"> <property name="userPreferences" ref="userPreferences"/></bean>
在前面的例子中,singleton bean(userManager)被HTTP Session-scope bean(userPreferences)注入引用。這裏的要點是,userManager bean是一個單例:每一個容器只實例化一次,而且它的依賴項(在本例中只有一個,userPreferences
bean)也只注入一次。這意味着userManager bean只對徹底相同的userPreferences對象(即最初注入它的對象)操做。
當一個生命週期較短的做用域bean注入到一個生命週期較長的做用域bean時(例如,將一個HTTP Session做用域的依賴bean做爲依賴項注入到singleton bean中),這不是您想要的結果。相反,須要一個single userManager對象,而且在HTTP Session的生命週期中,須要一個指定的HTTP Session的userPreferences對象。所以,容器建立一個對象,該對象公開與UserPreferences類徹底相同的public接口(理想狀況下是一個UserPreferences實例的對象),該對象能夠從做用域構造(HTTP request, Session等)獲取真正的UserPreferences對象。容器將此代理對象注入到userManager bean中,而userManager bean不知道此UserPreferences引用是代理。在本例中,當一個UserManager實例調用依賴注入的UserPreferences對象上的方法時,它其實是在代理上調用一個方法。而後,代理從(在本例中)HTTP會話獲取真實的UserPreferences對象,並將方法調用委託給檢索到的真實UserPreferences對象。
所以,在將 request-scoped
和session-scoped
的bean注入依賴對象時,須要如下配置(正確且完整),以下例所示:
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"> <aop:scoped-proxy/></bean>
<bean id="userManager" class="com.something.UserManager"> <property name="userPreferences" ref="userPreferences"/></bean>
選擇建立的代理類型
默認狀況下,當Spring容器爲用<aop:scoped-proxy/>標記的bean建立代理時,將建立一個基於CGLIB的類代理。CGLIB代理只攔截公共方法調用!不要對這樣的代理調用非公共方法。它們不會委託給做用域的實際目標對象。
或者,能夠配置Spring容器來爲此類做用域bean建立標準的基於JDK接口的代理,經過爲<aop:scoped-proxy/>的proxy-target-class的值指定false。使用基於JDK接口的代理意味着在應用程序classpath 中不須要額外的lib庫來影響這樣的代理。然而,這也意味着做用域bean的類必須實現至少一個接口,而且做用域bean被注入的全部依賴項必須經過其接口之一引用bean。如下示例顯示基於接口的代理:
<!-- DefaultUserPreferences 實現了UserPreferences 接口--><bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session"> <aop:scoped-proxy proxy-target-class="false"/></bean>
<bean id="userManager" class="com.stuff.UserManager"> <property name="userPreferences" ref="userPreferences"/></bean>
對代理不理解就須要去看代理實現有幾種。
有關選擇基於類或基於接口的代理的詳細信息,明天講。
關注Spring 中文社區 :
本文分享自微信公衆號 - Spring中文社區(gh_81d233bb13a4)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。