• 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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 多实例 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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

    区别

    Spring - IoC 容器之 Bean 的生命周期 这篇文章中,我们知道 Bean 初始化的时候,会默认调用无参构造器进行初始化,所以我们在无参构造器中输出一句话来观察 单实例 Bean多实例 Bean 的调用时机:

    @Bean
    public Person person() {
      	System.out.println("给容器中添加 person Bean");
        return new Person();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    Person person2 = applicationContext.getBean(Person.class);
    Person person3 = applicationContext.getBean(Person.class);
    System.out.println(person2 == person3);
    
    • 1
    • 2
    • 3

    从输出结果和 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    接着定义一个 单实例 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    比较容器中获取到的 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    结果说明 单实例 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    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());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出结果为:

    // 容器初始化的时候调用 person 的无参构造器,并没有注入 pet
    true	// 比较两次获取的 person,都是单例 bean,所以为 true
    pet create	// 多例bean 每次获取都是新创建的
    pet create	// 多例bean 每次获取都是新创建的
    false	// 多例bean所以为false
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要注意的是:@Lookup 源码中提到,不能加在 @Bean 方法上,也就是说以 @Bean 方式注入到容器中的 Bean@Lookup 注解会不起作用

  • 相关阅读:
    如何做好电子内窥镜的网络安全管理?
    OpenResty学习笔记03:再探WAF
    笔记记录--基于ccpd数据集利用Paddle OCR训练车牌检测模型
    MySQL-8.0 事务隔离级别
    流量治理最大的痛点-资源利用率上不去
    718. 最长重复子数组
    用三智者交易策略澳福加减仓轻松盈利,就是这么厉害
    一篇超级最全的python基础篇(新手必看!)
    React中redux、react-redux、@reduxjs/toolkit状态管理库的使用方式
    Android原生实现控件点击弹起效果方案(API28及以上)
  • 原文地址:https://blog.csdn.net/qiaohao0206/article/details/126035642