spring如何注入作用域不同的bean

Spring IoC容器不僅管理對象(bean)的實例化,而且還管理協作者(或依賴項)的連接。 如果要將(例如)HTTP請求範圍的Bean注入(例如)另一個作用域更長的Bean,則可以選擇注入AOP代理來代替已定義範圍的Bean。 也就是說,您需要注入一個代理對象,該對象公開與範圍對象相同的公共接口,但也可以從相關範圍(例如HTTP請求)中檢索實際目標對象,並將方法調用委託給實際對象。

您還可以在範圍爲單例的bean之間使用<aop:scoped-proxy />,然後引用通過經過可序列化的中間代理,從而能夠在反序列化時重新獲得目標單例bean。

當針對範圍原型的bean聲明<aop:scoped-proxy />時,共享代理上的每個方法調用都會導致創建新的目標實例,然後將該調用轉發到該目標實例

同樣,作用域代理不是以生命週期安全的方式從較短的作用域訪問bean的唯一方法。 您也可以將注入點(即構造器或setter參數或自動裝配的字段)聲明爲ObjectFactory ,從而允許getObject()調用在需要時按需檢索當前實例。 實例或將其單獨存儲。

作爲擴展的變體,您可以聲明ObjectProvider ,它提供了幾個附加的訪問變體,包括getIfAvailable和getIfUnique。

JSR-330的這種變體稱爲Provider,並與Provider 聲明和每次檢索嘗試的相應get()調用一起使用。 有關JSR-330總體的更多詳細信息,請參見此處。

以下示例中的配置僅一行,但是瞭解其背後的“原因”和“方式”很重要:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <aop:scoped-proxy/> 
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.something.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>

要創建這樣的代理,需要將一個子aop:scoped-proxy元素插入到有作用域的bean定義中(參見選擇要創建的代理類型和基於XML模式的配置)。</aop:scoped-proxy>爲什麼在請求、會話和自定義範圍級別定義作用域的bean需要aop:作用域代理元素?</aop:作用域代理>考慮一下下面的單例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>

在前面的示例中,單例bean(userManager)注入了對HTTP會話作用域bean(userPreferences)的引用。 這裏的重點是userManager bean是單例的:每個容器僅實例化一次,並且其依賴項(在這種情況下,僅一個,userPreferences bean)也僅注入一次。 這意味着userManager bean僅在完全相同的userPreferences對象(即最初與之注入對象)上操作。

將壽命較短的作用域bean注入壽命較長的作用域bean時,這不是您想要的行爲(例如,將HTTP“會話”作用域的協作bean作爲依賴項注入到singleton bean中)。 相反,您只需要一個userManager對象,並且在HTTPSession的生命週期中,您需要一個特定於HTTPSessionuserPreferences對象。 因此,容器創建了一個對象,該對象公開了與UserPreferences類完全相同的公共接口(理想情況下是一個UserPreferences實例的對象),該對象可以從範圍界定機制(HTTP請求, “會話”,依此類推)。 容器將這個代理對象注入到“ userManager” bean中,這並沒有意識到“ UserPreferences”引用是一個代理。 在這個例子中,當一個UserManager實例在依賴項注入的UserPreferences對象上調用一個方法時,它實際上是在代理上調用一個方法。 然後,代理從(在這種情況下)HTTPSession獲取實際的“ UserPreferences”對象,並將方法調用委託給檢索到的實際“ UserPreferences”對象。

<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代理僅攔截公共方法調用! 不要在此類代理上調用非公共方法。 它們沒有被委派給實際的作用域目標對象。

另外,您可以通過爲<aop:scoped-proxy />元素的proxy-target-class屬性值指定false,來配置Spring容器爲此類作用域的bean創建基於標準JDK接口的代理。 使用基於JDK接口的代理意味着您不需要應用程序類路徑中的其他庫即可影響此類代理。 但是,這也意味着作用域Bean的類必須實現至少一個接口,並且作用域Bean注入到其中的所有協作者必須通過其接口之一引用該Bean。 以下示例顯示基於接口的代理:

<!-- DefaultUserPreferences implements the UserPreferences interface -->
<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>

英文文檔地址:

https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#beans-factory-scopes-other-injection

中英文對照
https://www.qqkj.vip/archives/scoped-beans-as-dependencies

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章