理解 Scope
Scope 表示 Spring bean 的作用范围,指明了 bean 的生命周期。
Spring 中有哪些 Scope
Spring 中内置了一些 Scope,并且用户还能够扩展自己的 Scope,Spring 中内置的 Scope 如下,它们分别用在不同的场景中。
Spring 中的 Scope 大致可以分为两类,一类是在任何环境下都支持的 singleton、prototype,另一类是在 web 环境下才支持的 request、session、application、websocket。web 环境下的 scope 都是由 Spring 自定义实现。
如何配置 Scope
Spring 配置元数据包括 xml、properties、注解等,其中注解现在使用最多。scope 是 bean 定义的一个属性,xml 和 注解使用方式分别如下。
xml 配置 Scope
xml 是 Spring 早期使用较多,配置方式如下。
<bean class="com.mypackage.MyBean" scope=""> <aop:scoped-proxy proxy-target-class="false" /> </bean>
bean 标签中的 scope 属性用来指定作用域。
注解配置 Scope
Spring 中存在一个名为 @Scope 的注解,完整限定符为org.springframework.context.annotation.Scope,@Scope 可以配置在表示 bean 的类或方法上。使用方式如下。
@Scope(scopeName = "", proxyMode = ScopedProxyMode.DEFAULT) @Component public class Config { @Bean @Scope(scopeName = "", proxyMode = ScopedProxyMode.DEFAULT) public Object bean() { return new Object(); } }
scopeName 表示 scope 的名称。proxyMode 表示代理方式,根据配置可能会生成代理对象,可用于在 singleton bean 中注入非 singleton bean。
自定义 Scope
Spring 对自定义 Scope 的使用
scope 主要影响 Spring 对 bean 的获取。AbstractBeanFactory#doGetBean 方法获取 bean 时有关 Scope 的部分代码如下。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { ...省略部分代码 // Create bean instance. if (mbd.isSingleton()) { //获取singleton bean sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { //获取 prototype bean // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { //获取自定义scope的bean String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { ... 省略部分代码 } } ... 省略部分代码 }
Spring 在获取 bean 时,先判断 scope 是否为singleton,如果是则获取单例对象。
否则判断 scope 是否为 prototype 类型,如果是则会创建一个新对象。
如果不是 singleton 或 prototype ,Spring 会从自定义的 scope 中获取 bean。
Spring BeanFactory 使用了一个 map 保存注册的 Scope,和 @Scope 注解相同名称的 Scope 是一个接口,其完整的限定名为
org.springframework.beans.factory.config.Scope。web 环境中的 Scope 值 request、session、application 均是使用自定义的 Scope 实现,它们分别为 RequestScope、SessionScope 和 ServletContextScope。为了向 Spring 中添加自定义的 Scope ,需要调用 ConfigurableBeanFactory#registerScope 接口进行注册。
Scope 接口定义
先看 Scope 接口的定义。
public interface Scope { // 获取 Scope 中保存的 bean Object get(String name, ObjectFactory<?> objectFactory); // 移除 Scope 中保存的 bean @Nullable Object remove(String name); // 注册 bean 销毁时的回调 void registerDestructionCallback(String name, Runnable callback); // 解析上下文中的 key,web 环境下有使用 @Nullable Object resolveContextualObject(String key); // 获取会话 ID,web 环境下有使用 @Nullable String getConversationId(); }
Scope 中保存了作用域范围内的 bean,并提供了获取、移除、注册销毁回调等方法。
总结
Spring 中原生支持 singleton、prototype 作用域,web 环境下 Spring 通过自定义作用域实现了 request、session、application。通过自定义 Scope,Spring Cloud 甚至实现了 @Value 的刷新,感兴趣的小伙伴可自行查阅相关资料。