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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

古柏树下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值