spring中bean的scope设置为request、session,然后注入到其他bean中使用

先了解一下request和session这两个作用域是干嘛的

以下是官方文档中文翻译:

请求作用域

考虑如下的bean定义:

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

对于每个http请求,Spring容器会创建一个 LoginAction bean 的新实例。也就是说,loginAction bean 的作用域限于 HTTP 请求范围。 你可以在请求内随意修改这个bean实例的状态,因为其他 loginAction bean实例看不到这些变化,bean实例是与特定的请求相关的。 当请求处理完毕,对应的bean实例也就销毁(被回收)了。

会话作用域

考虑如下的bean定义:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

在每个HTTP Session的生命周期内,Spring容器会根据id为 userPreferences 的bean定义创建一个 UserPreferences bean 的新实例。 也就是说,userPreferences bean 的作用域限于 HTTP Session范围。和请求作用域 request-scoped bean 类似, 因为每个会话域 session-scoped bean的范围限于特定的 HTTP Session 内部,所以一个 Session 内的 userPreferences bean也是可以被随意修改, 而不会影响到其他 Session 中的 userPreferences bean。当一个HTTP Session 最终用完被JVM回收时,相关的会话域 session-scoped bean也被一起回收。

上面都是spring官方文档讲的关于request和session的作用

这里就不讲使用request session这些作用域要在web.xml中做什么了,这些对于使用spring mvc的我来说没什么用。

下面讲讲当作用域设置为这两个时,注入到其他bean中怎么使用,以及为什么可以这样用。

怎么使用?

scope为request时:

假设有bean两个,A和B,A的scope设置为request,B的scope随意,在spring配置文件中如下:
........
<bean class="*.*.A" scope="request"></bean>
<bean class="*.*.B"></bean>
........

此时,B要注入A这个属性,如下:
public class B{
 @Autowired
 private A a;
 //...........
}
如果就这么使用,spring会抛异常(是什么异常在这里不重要,反正是你写错了)

根据前面贴出的关于request和session的官方文档解释,应该知道这两个作用域的一些知识了。
在这里A的scope为request,说明每个请求拥有一个A的实例,
而B默认是单例的,那么,当A注入到B中,岂不是B的a属性只被注入为web应用的第一个request对象?之后的新的request不会再注入给a了。B是单例的,在mvc中,B就是controller,那B的a不可能永远是使用同一个request。。所以,当出现这种状况之后,spring直接给你抛异常

spring自然有解决之道:
只要修改spring配置文件(也就是把bean A修改一下)

........
<bean class="*.*.A" scope="request">
 <aop:scoped-proxy/>
</bean>
<bean class="*.*.B"></bean>
........

就仅仅在A中加了 <aop:scoped-proxy/>标签,就OK了。


为什么加<aop:scoped-proxy/>标签就可以了呢?

因为这个标签就是提示spring给A这个bean创建一个代理对象。
B要注入A时,如下:
public class B{
 @Autowired
 private A a;
 //...........
}
其实注入的是spring给A创建的代理对象。
每个请求都会创建一个A对象,然后由A的代理对象来判断当前使用的实际对象时哪个。(太拗口了)

大概原理是这样吧:

当你使用代理对象的任何方法时,会根据你 当前所处线程获得对应的请求(每个请求都是开启一个线程,用ThreadLocal来做)

这样spring就可以通过  你当前所处线程来获得对应的请求。

再从请求中获得实际使用的bean,再调用该bean中与代理对象对应的方法。

这样,代理对象在整个spring上下文中只有一个,但通过代理对象使用的实际对象是属于当前请求范围内的。

简而言之,scope为request的bean,在每个请求到来时,会创建一个A的实际对象与当前的请求 对应。而使用A的代理对象时,会通过某种方式获得当前的请求,再根据当前这个请求,获得对应A的实际对象。使用代理对象间接操控的就是与当前请求对应的A的实际对象。

request作用域就是以上那样。
至于session作用域,和request作用域差不多。
因为web中的会话不就是从请求中获得么。。。

不管看不看得懂我说的,我还是贴出对应的官方文档中文翻译吧,如下:


将上述作用域的bean作为依赖

Spring IoC 容器不仅负责管理对象(beans)的创建,也负责有合作(依赖)关系对象之间的组装。 如果你想将一个HTTP 请求作用域的bean注入到另一个 bean,必须注入一个 AOP 代理来取代请求作用域的bean本身。 也就是说,你得有一个和作用域bean实现了相同接口、能在相应作用域(例如,HTTP 请求) 访问真正的请求域bean目标对象的代理对象,而且这个代理对象要能把方法调用委派给真正的请求域bean, 然后把代理对象注入到请求域bean该注入的地方。

[Note]

对于作用域为 singletons (单例)或 prototypes(原型)的bean , 不需要 使用 <aop:scoped‐proxy/> 元素

下述代码虽然只有一行,但读者不仅要“知其然”,更要“知其所以然”。

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


    <!-- 将一个HTTP Session bean 暴露为一个代理bean -->
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">

        <!-- 通知Spring容器去代理这个bean -->
        <aop:scoped-proxy/>
    </bean>


    <!-- 将上述bean 的代理注入到一个单例bean -->
    <bean id="userService" class="com.foo.SimpleUserService">

        <!-- 引用被代理的 userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>

为了创建上文提到的代理对象,要在 request 请求、 session 会话、 globalSession 全局会话 和自定义作用域的bean声明中加入 <aop:scoped‐proxy/> 子元素。 (参考 the section called “选择要创建的代理类型” 和 Chapter 33, XML Schema-based configuration) 。为什么要这么做呢?让我们将这几个作用域的bean定义与单例作用域的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>

在上述例子中,将HTTP Session 会话域的 userPreferences bean 注入进了单例域 userManager。 代码的关键在于userManager  是个单例bean,它在每个Spring 容器中只会被初始化 一次, 它的依赖对象(在这个例子中是 userPreferences bean)也只会被注入一次。这就意味着 userManager   每次操作的都是在最开始注入进来的同一个 userPreferences 对象。

当你把一个生命周期较短的bean注入至一个生命周期较长的bean时,例如把HTTP Session bean注入到单例bean, 这种情况肯定 不是 你所期望的。相反,你希望有个唯一的 userManager 单例对象,在每个HTTP Session的生命周期内, 都能有一个专门的 userPreferences 对象供 userManager 使用。因此,Spring容器会创建一个代理类, 使之与 UserPreferences 类实现相同的接口(理论上也是一个 UserPreferences 对象),并能根据作用域(HTTP 请求、 Session,等等)去获得真正的 UserPreferences 对象。 Spring容器将这个代理类的对象注入到 userManager , 但是 userManager 并不清楚它获得的 UserPreferences 其实是个代理。 在这个例子中,当UserManager 实例调用注入的 UserPreferences 对象的某个方法时,实际上调用的是代理对象的方法。 然后,代理对象从HTTP Session 中获取、并将方法调用委派给真正的 UserPreferences 对象。

所以,当你需要将 request-session-, and globalSession-scoped 作用域的 bean 注入至其他合作者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>


其实这个原理与使用@Autowired注入HttpServletRequest是差不多了,都是注入代理对象,具体请看我写的另一片文章:

http://blog.csdn.net/qq_36951116/article/details/79116829

写着篇文章是为了防止自己以后忘了,到时候又要到处百度。

如果有错误,请指正,非常感谢!


评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值