美文网首页
Spring中的Bean是线程安全的吗?

Spring中的Bean是线程安全的吗?

作者: 程就人生 | 来源:发表于2022-07-01 21:09 被阅读0次

    这是一道迷幻题,既不能回答是,也不能回答不是。
    先回忆一下在Spring中是如何创建一个bean的?通过添加@Controller、@Service、@Repository 、@Component等注解在一个类上,添加@Bean在一个方法上...还有呢?
    创建一个单例、多例的Bean呢?
    默认就是单例的bean,要声明多例的通过@Scope注解进行配置。
    如果是一个多例Bean,我们还需要考虑线程安全吗?
    当然不需要。如果是一个单例Bean,我们一定要考虑线程安全吗?这,,,不一定吧!
    再来看看@Controller注解的源码:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Controller {
    
      /**
       * The value may indicate a suggestion for a logical component name,
       * to be turned into a Spring bean in case of an autodetected component.
       * @return the suggested component name, if any (or empty String otherwise)
       */
      @AliasFor(annotation = Component.class)
      String value() default "";
    
    }
    

    @Service的源码:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Service {
    
      /**
       * The value may indicate a suggestion for a logical component name,
       * to be turned into a Spring bean in case of an autodetected component.
       * @return the suggested component name, if any (or empty String otherwise)
       */
      @AliasFor(annotation = Component.class)
      String value() default "";
    
    }
    

    Repository 的源码:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Repository {
    
      /**
       * The value may indicate a suggestion for a logical component name,
       * to be turned into a Spring bean in case of an autodetected component.
       * @return the suggested component name, if any (or empty String otherwise)
       */
      @AliasFor(annotation = Component.class)
      String value() default "";
    
    }
    

    @Component的源码:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Indexed
    public @interface Component {
    
      /**
       * The value may indicate a suggestion for a logical component name,
       * to be turned into a Spring bean in case of an autodetected component.
       * @return the suggested component name, if any (or empty String otherwise)
       */
      String value() default "";
    
    }
    

    @Controller和@Service、@Repository最终都指向了@Component。@Component注解中有没有关于线程安全的?没有,它们几个并没有什么不同。接下来看看@Bean的源码:

    /**
     * bean注解
     * 作用在方法上 
     */
    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Bean {
      
      /**
       * bean的value,别名name
       * @return
       */
      @AliasFor("name")
      String[] value() default {};  
    
    
      /**
       * bean的name,别名value
       * @return
       */
      @AliasFor("value")
      String[] name() default {};
      
      /**
       * 是否自动装配默认false,已过期忽略不计
       * @return
       */
      @Deprecated
      Autowire autowire() default Autowire.NO;
      
      /**
       * 自动装配到其他bean的候选,默认true
       * @return
       */
      boolean autowireCandidate() default true;
      
      /**
       * 初始化方法,默认空
       * @return
       */
      String initMethod() default "";
      
      /**
       * 销毁方法,默认 AbstractBeanDefinition.INFER_METHOD
       * @return
       */
      String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
    
    }
    

    @Bean可用于方法上,它的name是value,value既是name。@Bean("xxx") 声明一个名为xxx的bean。initMethod是bean初始化时,所要执行的方法。同样也没有关于线程安全的。@Bean和以上几个注解的不同之处就是@Bean是作用在方法上的。而@Controller、@Service、@Repository、@Compoment是作用在类上的。再看下@Scope的源码:

    /**
     * Scope注解
     * 作用在类/接口/枚举、方法上
     * @Date
     */
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Scope {
    
      /**
       * 等同于 scopeName
       * @see #scopeName
       */
      @AliasFor("scopeName")
      String value() default "";
    
      /**
       * 默认单例
       * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
       * @since 4.2
       * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE  原型,每次创建一个新对象
       * @see ConfigurableBeanFactory#SCOPE_SINGLETON  单例,默认作用域
       * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST  每次http请求创建一个对象
       * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION  同一个会话共享一个实例
       * @see #value
       */
      @AliasFor("value")
      String scopeName() default "";
    
      /**
       * 代理模式,默认不需要
       * @see ScopedProxyMode
       */
      ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
    
    }
    

    @Scope,默认单例(一个spring容器中只创建一个实例),可设置原型(每次创建一个实例)、http请求(一个http请求一个实例)、会话(一个会话一个实例)。这个注解只是声明在Spring容器中实例化的数量,并没有关于线程安全的任何代码。多个实例,每个线程中的实例互不影响,因此多实例的bean是不需要考虑线程安全的。单例Bean又分为两种情况,无状态的bean和有状态的bean。无状态的bean也不需要考虑线程安全,比如service、controller、dao这些都是无状态的bean。有状态的bean,Spring官方提供的bean,通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等,可以看看它们的源码。因此在我们的项目里,如果涉及到有状态的单例Bean,单例bean中有成员变量的,可考虑使用ThreadLocal来保存单例中的成员变量,使线程之间的状态隔离,互不影响,从而达到线程安全的目的。

    最后总结Spring容器中的Bean是否线程安全,其实和Sping容器是没有什么关系的。Spring容器只负责创建和管理Bean,并未提供Bean的线程安全策略。因此,Spring容器中的Bean并不具备线程安全的特性。Spring中的Bean又可分为多例bean和单例bean,单例bean又可分为无状态的bean和有状态的bean。多例bean和无状态的bean是不需要考虑线程安全的。只有有状态的单例bean是需要考虑线程安全的,可使用ThreadLocal来管理成员变量,使之线程隔离,以达到线程安全的目的。

    相关文章

      网友评论

          本文标题:Spring中的Bean是线程安全的吗?

          本文链接:https://www.haomeiwen.com/subject/iffgvrtx.html