您的位置:首页 > 编程语言 > Java开发

Spring系列8:bean的作用域

2022-01-30 08:31 706 查看

本文内容

  1. bean定义信息的意义
  2. 介绍6种bean的作用域

bean定义信息的意义

Spring中区分下类、类定义信息,类实例对象的概念?不容易理解,以餐馆中点炒饭为例。

类: 相当于你看到菜单上炒饭这个菜品,有这个菜。

类定义信息:相当于炒饭的烹饪法,烹饪法只有一份

类实例对象: 相当于按照上面烹饪法炒出来的一份炒饭,可以炒分多份出来。

Spring容器中创建了一个类定义信息的,就可以根据这个定义信息来创建个类实例对象出来,这个理解了很关键。Spring中不仅可以控制 bean 对象中的各种依赖项和配置值,还可以控制 bean 额作用范围。

介绍6种bean的作用域

Spring Framework 支持6个bean的作用域,其中4个仅在web类型的

ApplicationContext
中可用。详细见下表

作用域 描述
singleton (默认)将单个 bean 定义限定为每个 Spring IoC 容器的单个对象实例。
prototype 将单个 bean 定义限定为任意数量的对象实例
request 将单个 bean 定义限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,该实例是在单个 bean 定义的后面创建的。仅在web类型的
ApplicationContext
有效。
session 将单个 bean 定义限定为 HTTP 会话的生命周期。仅在web类型的
ApplicationContext
有效。
application 将单个 bean 定义限定为 ServletContext 的生命周期。仅在web类型的
ApplicationContext
有效。
websocket 将单个 bean 定义限定为 WebSocket 的生命周期。仅在web类型的
ApplicationContext
有效。

从 Spring 3.0 开始,线程范围可用,但默认情况下未注册。

SimpleThreadScope
感兴趣可以详细看下面自定义作用域。

singleton

只有一个单例 bean 的共享实例被管理,并且所有对具有与该 bean 定义匹配的一个或多个 ID 的 bean 的请求都会导致 Spring 容器返回一个特定的 bean 实例。"蝎子粑粑独一份"。注意是一个IoC容器内。

prototype

原型作用域下每次容器都会创建一个新的 bean 实例。通常来说,对有状态 bean适合使用原型,对无状态 bean适合使用单例。

下面的配置节指定了bean的作用域是原型的。

<bean id="accountService" class="x.y.z.AccountService" scope="prototype"/>

与其他范围相比,Spring 不管理原型 bean 的完整生命周期。容器实例化、配置和以其他方式组装原型对象并将其传递给客户端,由客户端去管理。bean生命周期后面介绍。

request, session, application, webSocket

这4个在web的应用上下文中使用的作用域,暂不展开讲,留给后面写spring mvc 专门讲。

自定义作用域和使用

如何自定义?

可以通过实现

org.springframework.beans.factory.config.Scope
自定义作用域。
Scope
接口有四种方法可以从作用域中获取对象,将它们从作用域中移除,并让它们被销毁。

package org.springframework.beans.factory.config;
public interface Scope {
// 从底层范围返回具有给定名称的对象
Object get(String name, ObjectFactory<?> objectFactory);
// 从底层范围中删除具有给定的对象
Object remove(String name);
// 注册要在作用域中指定对象的销毁时执行的回调
void registerDestructionCallback(String name, Runnable callback);
// 省略
}

打铁要趁热,直接上

SimpleThreadScope
的源码,解析下线程级别作用域是如何实现的。

package org.springframework.context.support;

public class SimpleThreadScope implements Scope {

private static final Log logger = LogFactory.getLog(SimpleThreadScope.class);
// 1 bean是存放在ThreadLocal中的 绑定了当前线程
private final ThreadLocal<Map<String, Object>> threadScope =
new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};

// 2 查ThreadLocal,有对应bean就直接返回,没有就创建一个放入ThreadLocal,在返回
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = this.threadScope.get();
Object scopedObject = scope.get(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
scope.put(name, scopedObject);
}
return scopedObject;
}

// 3 从ThreadLocal中移除bean
@Override
@Nullable
public Object remove(String name) {
Map<String, Object> scope = this.threadScope.get();
return scope.remove(name);
}

// 4 销毁回调
@Override
public void registerDestructionCallback(String name, Runnable callback) {
logger.warn("SimpleThreadScope does not support destruction callbacks. " +
"Consider using RequestScope in a web environment.");
}

@Override
@Nullable
public Object resolveContextualObject(String key) {
return null;
}

@Override
public String getConversationId() {
return Thread.currentThread().getName();
}

}

如何使用?

  1. 先将自定义的scope注入到容器中

    编码的方式注册:

    ConfigurableBeanFactory
    接口提供了
    registerScope
    来注册自定义的scope。

    Scope threadScope = new SimpleThreadScope();
    beanFactory.registerScope("thread", threadScope);

    配置文件方式的注册: 使用

    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>
    </beans>
  2. bean配置中使用

    使用方式普通的scope一样。

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

总结

本文Spring中的7种作用域,以及如何自定义作用域并使用。下一篇介绍基于注解的Spring容器配置。

知识分享,转载请注明出处。学无先后,达者为先!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: