理解 Spring Bean 的作用域(Scope)

简介: 理解 ScopeScope 表示 Spring bean 的作用范围,指明了 bean 的生命周期。

理解 Scope


Scope 表示 Spring bean 的作用范围,指明了 bean 的生命周期。


Spring 中有哪些 Scope


Spring 中内置了一些 Scope,并且用户还能够扩展自己的 Scope,Spring 中内置的 Scope 如下,它们分别用在不同的场景中。


image.png

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 的刷新,感兴趣的小伙伴可自行查阅相关资料。


目录
相关文章
|
14天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
67 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
2月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
212 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
1月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
218 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
81 1
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
40 1
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
85 1
|
2月前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean