概述
-
单实例 Bean
Spring
的IoC 容器
默认创建的Bean
就是单实例
的,也就是说无论调用多少次,获取的都是同一个对象。// 定义一个 Bean @Bean public Person person() { return new Person("lili", 12); } // 多次获取容器中的 Bean,判断是否相等 Person person = applicationContext.getBean(Person.class); Person person1 = applicationContext.getBean(Person.class); System.out.println(person == person1); // true
-
多实例 Bean
Spring
提供了@Scope
注解来创建多实例 Bean
,多实例 Bean
每次从容器中获取Bean
都需要重新初始化一个@Bean //@Scope("prototype") @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Person person() { return new Person("lili", 12); } // 多次获取 Bean,并比较 Person person = applicationContext.getBean(Person.class); Person person1 = applicationContext.getBean(Person.class); System.out.println(person == person1); // false
区别
在 Spring - IoC 容器之 Bean 的生命周期 这篇文章中,我们知道 Bean
初始化的时候,会默认调用无参构造器进行初始化,所以我们在无参构造器中输出一句话来观察 单实例 Bean
和 多实例 Bean
的调用时机:
@Bean
public Person person() {
System.out.println("给容器中添加 person Bean");
return new Person();
}
Person person2 = applicationContext.getBean(Person.class);
Person person3 = applicationContext.getBean(Person.class);
System.out.println(person2 == person3);
从输出结果和 Person Bean
的构造器输出可以得出:
- 单实例
- 在容器启动的时候创建
- 无论调用多少次都只输出一次,说明获取的是同一个对象
- 多实例
- 容器启动的时候没有输出,说明
多实例 Bean
不是在容器启动的时候创建的 - 获取
Bean
的时候会输出,说明多实例 Bean
是在获取的时候创建的 - 每调用一次都会输出一次,说明
多实例 Bean
每次获取的Bean
都是新创建的
- 容器启动的时候没有输出,说明
单实例 Bean
中注入 多实例 Bean
首先定义一个 多实例 Bean
:
@Data
@AllArgsConstructor
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class Pet {
public Pet() {
System.out.println("pet create");
}
private String name;
}
接着定义一个 单实例 Bean
,并把这个 多实例 Bean
作为 单实例 Bean
的一个属性:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
@Autowired
private Pet pet;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
比较容器中获取到的 Person
和 Person
中的 pet
:
Person person = applicationContext.getBean(Person.class);
Person person1 = applicationContext.getBean(Person.class);
System.out.println(person == person1); // true
System.out.println(person.getPet() == person1.getPet()); // true
// 只会输出一次,说明多实例 Bean 只创建了一次
pet create
结果说明 单实例 Bean
中注入了 多实例 Bean
,那么无论创建了多少个单实例Bean,其中的多实例Bean属性都是第一次创建单实例Bean时创建的多实例Bean
如果需要每次获取都是新的多实例Bean,可以使用 @Lookup
注解标注在 set 方法上:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Person {
private String name;
private Integer age;
// @Autowired
private Pet pet;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Lookup
public Pet getPet() {
return pet;
}
}
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config1.class);
Person person = applicationContext.getBean(Person.class);
Person person1 = applicationContext.getBean(Person.class);
System.out.println(person == person1);
System.out.println(person.getPet() == person1.getPet());
输出结果为:
// 容器初始化的时候调用 person 的无参构造器,并没有注入 pet
true // 比较两次获取的 person,都是单例 bean,所以为 true
pet create // 多例bean 每次获取都是新创建的
pet create // 多例bean 每次获取都是新创建的
false // 多例bean所以为false
需要注意的是:
@Lookup
源码中提到,不能加在@Bean
方法上,也就是说以@Bean
方式注入到容器中的Bean
,@Lookup
注解会不起作用