【问题标题】:Is a spring bean created with NEW really a singleton用NEW创建的spring bean真的是单例吗
【发布时间】:2019-06-04 17:22:47
【问题描述】:

我在这样的配置类中创建了一个 spring bean:

@Bean
MyClass getMyClass() {
    MyClass mc = new MyClass()
    return mc;
}

每当MyClass 自动装配到另一个需要注入它的类中时,它是否总是会凭借bean 定义中的new 创建一个新对象? 以这种方式创建的 bean 是真正的单例吗?

【问题讨论】:

    标签: spring spring-bean spring-ioc


    【解决方案1】:

    Spring 保证无论您在带有Bean 注释的方法中执行的任何操作,都只会执行一次。内部 Spring 工厂会处理这个问题。

    当然它取决于scope,但默认范围是singleton。请参阅文档:

    1. Scopes
    2. Bean
    3. Is spring default scope singleton or not?

    应该帮助您了解其工作原理的小示例:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.time.LocalDateTime;
    import java.util.Random;
    
    @Configuration
    public class SpringApp {
    
        public static void main(String[] args) {
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringApp.class);
    
            System.out.println(ctx.getBean(MyClass.class));
            System.out.println(ctx.getBean(MyClass.class));
            System.out.println(ctx.getBean(MyClass.class));
            System.out.println(ctx.getBean(MyClass.class));
        }
    
        @Bean
        public MyClass getMyClass() {
            System.out.println("Create instance of MyClass at " + LocalDateTime.now());
            MyClass myClass = new MyClass();
    
            return myClass;
        }
    }
    
    class MyClass {
    
        private int value = new Random().nextInt();
    
        @Override
        public String toString() {
            return super.toString() + " with values = " + value;
        }
    }
    

    打印:

    Create instance of MyClass at 2019-01-09T22:54:37.025
    com.celoxity.spring.MyClass@32a068d1 with values = -1518464221
    com.celoxity.spring.MyClass@32a068d1 with values = -1518464221
    com.celoxity.spring.MyClass@32a068d1 with values = -1518464221
    com.celoxity.spring.MyClass@32a068d1 with values = -1518464221
    

    当您定义具有范围protoype的bean时

    @Scope("prototype")
    @Bean
    public MyClass getMyClass()
    

    应用打印:

    Create instance of MyClass at 2019-01-09T22:57:12.585
    com.celoxity.spring.MyClass@282003e1 with values = -677868705
    Create instance of MyClass at 2019-01-09T22:57:12.587
    com.celoxity.spring.MyClass@7fad8c79 with values = 18948996
    Create instance of MyClass at 2019-01-09T22:57:12.587
    com.celoxity.spring.MyClass@71a794e5 with values = 358780038
    Create instance of MyClass at 2019-01-09T22:57:12.587
    com.celoxity.spring.MyClass@76329302 with values = 868257220
    

    【讨论】:

      【解决方案2】:

      在你的例子中,是的。

      实际上会发生的是,当 Spring 启动时,它将调用 getMyClass() 方法,该方法将新建一个对象实例。然后 Spring 将保留该单个实例并将其注入到所有其他需要 MyClass 实例的 bean 中。

      它将以这种方式工作,因为您没有在 bean 上声明范围 - 默认是单例,如另一个答案所示。

      【讨论】:

        【解决方案3】:

        Spring 的单例 bean 的概念与 单例模式。

        Spring 单例的范围最好按照容器来描述 每颗豆

        Section 4.4.1

        这意味着如果您创建一个 spring bean,该 bean 将在 IoC Spring Container. 中提供其生命周期 如果你想用“NEW真的是一个单例bean”来创建。你可以通过一个新的 Spring Ioc Container 来实现它。 让我通过一个例子来说明这一点。

         public static void main(String[] args) {
                ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringApp.class);
                ApplicationContext ctxReallyNewOne = new AnnotationConfigApplicationContext(SpringApp.class);
                ApplicationContext ctx2ReallySecondOne = new AnnotationConfigApplicationContext(SpringApp.class);
        
                System.out.println(ThreadColors.Red + "Beans created via same Ioc container with same class");
                System.out.println(ctx.getBean(MyClass.class));
                System.out.println(ctx.getBean(MyClass.class));
        
                System.out.println(ThreadColors.Cyan + "Beans created via different Ioc container with SAME CLASS");
                System.out.println(ctxReallyNewOne.getBean(MyClass.class));
                System.out.println(ctx2ReallySecondOne.getBean(MyClass.class));
        
            }
        

        运行此代码后,控制台将写入以下内容。

        另外如果你想获取一个bean的创建日期。不需要使用LocalDataTime.now()。为此,最好使用“Spring Beans 的生命周期”。

        The Lifecycle of Spring Beans

        【讨论】:

          猜你喜欢
          • 2019-07-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-14
          • 1970-01-01
          • 2012-05-30
          • 1970-01-01
          相关资源
          最近更新 更多