spring学习(六)——spring官方文档阅读(5.0.7)——Bean的scopes

Bean的scopes

spring定义了六种scope,其中有四种只在web应用有效,在<bean/>中使用scope属性指定值:

1、singleton(默认):单例,每个IOC容器为每个bean创建一个唯一的实例

2、prototype:在需要某个实例时(例如作为某个bean的依赖,使用getBean函数),创建一个新实例,spring不会管理这种bean的生命周期,所以我们必须自己管理bean的销毁与资源的释放,可以使用BeanPostProcessor来让容器释放资源

3、request:对每个HTTP请求创建一个新实例,仅在web应用中有效,当一次请求服务结束时,这个bean就会被销毁,除了在<bean/>中使用scope外,也可以使用注解@RequestScope

4、session:对每个HTTP连接创建一个新实例,仅在web应用中有效,当会话有效期到期时,这个bean就会被销毁,关于request与session的区别,请见:https://stackoverflow.com/questions/3106452/how-do-servlets-work-instantiation-sessions-shared-variables-and-multithreadi/3106909#3106909,可以使用注解@SessionScope

5、application:每个web应用创建一个新实例,仅在web应用中有效,这与singleton有一点相似,但又不同,application在一个web服务中只有一个,一个web服务可以有多种IOC容器,假设每个容器都配置了bean A,则每个容器都有且仅有一个bean A,可以使用注解@ApplicationScope

6、websocket:为使用websocket协议(实现client与server的双向通信)通信的连接创建一个新实例,仅在web应用中有效

如果使用后四个scope,需要初始化web配置,具体与我们的servlet环境有关,如果我们使用springMVC,则不需要配置,因为DispatcherServlet已经帮我们做好了相应的配置,对于servlet 3.0+,可以通过WebApplicationInitializer接口自动初始化,对于老旧的容器,在web.xml中配置如下信息:

<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

如果上述配置存在问题,可以使用RequestContextFilter过滤器:

<web-app>
    ...
    <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...
</web-app>

RequestContextFilter,RequestContextListener,DispatcherServlet做的都是相同的事,将HTTP请求与对应的服务线程绑定

当bean的依赖的作用域与bean本身不同时,可以考虑注入代理(是一个接口),而不是依赖bean,AOP代理的方法与依赖bean的方法一致,但是它可以从相应范围中检索真正的依赖bean对象,调用其中的方法,举个例子:

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

</bean>

<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

userManager的生命周期比userPreferences长,当userManager创建时,容器会创建一个userPreferences对象进行注入,以后每次操作都使用同一个userPreference,而且userPreference的生命周期比userManager短,意味着userManager引用userPreference时可能为空,并且这也不是我们要的效果,我们想将userPreferences(与一个客户端连接的HTTP会话)作为userManager的一个属性(上述方式注入的userPreference不是与客户端连接的HTTP会话),此时可以使用AOP代理,把上述配置改为如下:

<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>

此时userManager注入的是userPreferences的代理对象,当userManager使用userPreferences时,代理对象将会在对应范围查找(本例中为http会话)实际对象(可能会找不到),将方法调用委托给检索到的实际对象,什么叫到对应范围查找呢?怎么找?,例如被代理Bean的scope为prototype,则每次调用被代理对象的方法都会创建一个全新的被代理对象,若被代理Bean的scope为session,则根据sessionID去查找session范围的Bean,如果查找到,就使用这个Bean,将方法交由它处理,否则,就重新创建一个,总之就是不会出现空指针的情况。

<aop:scoped-proxy/>使用CGLIB,CGLIB只能代理public方法,可以将上述配置改一下:
 

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

此时使用的是JDK代理,JDK代理只能代理接口方法,DefaultUserPreferences将会继承一个接口,userManager的userPreferences、setter方法形参的类型应该改为接口的类型,否则会提示类型不匹配(由于JDK代理的特点)

想要在xml中使用上述标签,需要在xml中更改beans标签(添加了aop的命名空间):

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop 
    	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
        >

我们也可以自定义scope或是覆盖spring的scope,只需要继承并实现org.springframework.beans.factory.config.Scope接口,Scope接口的详细内容:https://docs.spring.io/spring-framework/docs/5.0.7.RELEASE/javadoc-api/org/springframework/beans/factory/config/Scope.html

猜你喜欢

转载自blog.csdn.net/dhaiuda/article/details/81900815
今日推荐