【问题标题】: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。请参阅文档:
- Scopes
- Bean
- 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