Spring - 单实例Bean 和 多实例Bean 的不同

概述

  • 单实例 Bean

    SpringIoC 容器 默认创建的 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;
    }
}

比较容器中获取到的 PersonPerson 中的 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 注解会不起作用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值