Bean作用域

控制bean在容器内的存在

Spring提供6种作用域,其中4种只能用于WEB应用:

singleton默认的作用域,每个容器内只有一个实例;
prototype请求一次就创建一个新的实例;
requestbean生命周期绑定单个http请求,每个http请求都有自己的实例。
仅在用于web的容器内有效;
sessionbean生命周期绑定单个http会话。仅在用于web的容器内有效;
applicationbean生命周期绑定单个ServletContext。仅在用于web的容器内有效;
websocketbean生命周期绑定单个WebSocket。仅在用于web的容器内有效;

注意:在prototype作用域中,虽然生命周期方法会被调用,但是销毁的方法不会被调用,用户必须手动释放并清理这种bean打开的资源。可以考虑定制一个 bean post-processor。某些时候prototype作用域仅仅是替代了new操作符。

requestsessionapplication, 和websocket作用域都和webMVC相关。

Singleton作用域

容器内只有一个bean实例,请求这个bean都返回同一个实例。这种Singleton的bean被存储于cache中。

Singleton示意图

Spring的Singleton最好称做一个容器一个bean,不同容器之间互不影响。

这也是Spring的默认作用域,在xml文件中定义:

<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- 上下等价 -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

Prototype作用域

每次请求这个bean都创建一个新的实例。
一个常用的规则是:将prototype作用域用于有状态的bean,而singleton用于无状态的bean。

Prototype示意图

为了和别的作用域做出区分,Spring并不完全接管prototype bean的生命周期,
容器只管实例化、设置或者装配,交由用户之后就不再管。所以销毁生命周期调用不会生效。
用户必须手动释放已经打开的资源。

如果还是想让Spring容器释放资源,可以考虑使用一个定制的bean post-processor。
某些时候prototype作用域仅仅是替代了Java的new操作符。

Singleton的bean有Prototype的依赖

Prototype的bean注入Singleton的bean,只要创建一个新的实例就好,但是反过来会比较麻烦。
这时候需要使用方法注入。

定制一个作用域

bean作用域机制是可拓展的,可以定义自己的作用域,但是不可以覆盖内置的作用域。

创建一个作用域:实现org.springframework.beans.factory.config.Scope接口。

Scope接口有四个方法,可以获得、移除和销毁bean:

Object get(String name, ObjectFactory<?> objectFactory);
Object remove(String name);
void registerDestructionCallback(String name, Runnable destructionCallback);
String getConversationId();

使用定制作用域:通过ApplicationContext的registerScope方法注册自己的作用域:

void registerScope(String scopeName, Scope scope);

如果在xml配置里使用定制作用域的话需要通过CustomScopeConfigurer类注册作用域:

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

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="org.springframework.context.support.SimpleThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="thing2" class="x.y.Thing2" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="thing1" class="x.y.Thing1">
        <property name="thing2" ref="thing2"/>
    </bean>

</beans>

注意:把<aop:scoped-proxy/>放在实现FactoryBean接口的<bean>声明内,
意思是这个bean自己的作用域,而不是它返回的bean的作用域。

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注