环境:SpringBoot3.2.5
1. 简介
本篇文章旨在深入探讨将原型(Prototype)作用域的bean注入到单例(Singleton)作用域bean中的多种策略,并详细剖析每种方法的优缺点。在Spring框架中,默认情况下bean是单例的,这意味着它们在应用的生命周期内仅被创建一次并共享。然而,当需要将原型bean(每次请求都创建一个新实例)注入到单例bean中时,就会出现作用域注入问题。如下示例:
在该配置类中,将PrototypeBean定义为原型bean,SingletonBean定义为单例,该单例bean的定义如下:
在这通过@Resource注解,注入原型bean。接下来我们通过如下的代码进行测试:
以上调用三次test方法,我们期望的是每次调用打印的prototypeBean属性都不是同一个,结果如下:
三次打印都是同一个。这种不同作用域的注入该如何解决呢?接下来我将介绍8种方式解决。
2. 解决办法
2.1 单例bean实现ApplicationContextAware
每调用test方法时都会通过ApplicationContext对象从容器中获取新实例。
缺点:
- 这种方法有个严重的缺点。它违背了控制权倒置的原则,因为我们直接向容器请求依赖关系。
- 在SingletonBean类中通过applicationContext获取原型 Bean。这意味着将代码与 Spring 框架耦合。
除了这2缺点,用起来感觉还行啊
2.2 通过jakarta的Provider注入
这里通过jakarta.inject包中的Provider接口注入原型bean。每次调用get方法都会从容器中再次获取一个新的实例对象。
2.3 借助Spring提供的ObjectProvider接口
直接注入ObjectProvider接口对象,该接口提供了丰富的方法,不仅仅可以获取单个对象,也可以获取多个对象(集合),包括如下可用方法:
图片
2.4 借助Spring的ObjectFactory接口
该接口与上面ObjectProvider接口挺像,但是ObjectFactory是函数式接口只有一个getObject方法,并且只能返回一个对象。
2.5 使用@Lookup注解
Spring 会将SingletonBean创建为代理对象并覆盖使用 @Lookup 注解的 get() 方法。每当我们调用get() 方法时,它会返回一个新的 PrototypeBean 实例。
2.6 注册Function类型的Bean
这里注册了Function类型的bean对象,而具体会是什么类型完全由使用的地方决定。
通过定义Function类型的bean,这里我们就可以不限制具体是那个类型的原型bean,这就非常的通用了。
2.7 作用域代理
在@Scope注解上配置proxyMode=TARGET_CLASS这样配置以后,容器将会为PrototypeBean创建代理对象。也是能够保证每次获取的都是新的实例对象。
2.8 使用@Lazy注解
通过@Lazy注解后,这里注入的PrototypeBean将会是一个代理对象,这样也可以保证每次使用时都是新的实例对象。