单实例 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
注解会不起作用