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