别再使用 @Lazy 了,试试 ObjectFactory 和 ObjectProvider
前言
@Lazy 可以解决某些特殊场景下的循环依赖问题。
在翻阅 @Lazy 的源码注释时,得知 @Lazy 可以通过 ObjectFactory 和 ObjectProvider 来进行替代。
下面我们就来看看 ObjectFactory 和 ObjectProvider
的原理和使用吧…
@Lazy 相关的知识:
@Lazy为什么可以解决特殊的循环依赖问题
@Lazy延迟加载与延迟注入有什么区别
Spring 版本
Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)
正文
ObjectProvider 作用分析
ObjectProvider 的类图如下:
ObjectProvider 提供的是通过 ObjectProvider#getObject()
或者 ObjectProvider#getIfAvailable()
获取 Object (即: bean) 的能力。
也就是说,在依赖注入时,注入的是一个 ObjectProvider 对象,想要获取到真正的 bean 时,可以通过调用 ObjectProvider#getObject()
或者 ObjectProvider#getIfAvailable()
。
所以,它跟 @Lazy 起到的作用是很相似的。
Spring 对 ObjectProvider 依赖的处理
Spring 在 populateBean
时,会处理依赖属性的注入,最终会调用 DefaultListableBeanFactory#resolveDependency()
来对依赖进行解析。
相关的源码如下:
可以看到,如果是 ObjectFactory 和 ObjectProvider
类型的依赖,那么会直接 return new DependencyObjectProvider(descriptor, requestingBeanName);
,而不会触发依赖 bean 的加载。
等到真正使用 ObjectProvider#getObject()
获取 bean 的时候,才会触发 bean 的加载。
ObjectProvider 的使用场景
ObjectProvider 大量出现在 SpringBoot 的 Configuration 配置类中,做为构造函数的入参来进行使用,即构造注入依赖。
例如 MybatisPlus 中有如下代码:
public MybatisPlusAutoConfiguration(MybatisPlusProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider,
ObjectProvider<TypeHandler[]> typeHandlersProvider,
ObjectProvider<LanguageDriver[]> languageDriversProvider,
ResourceLoader resourceLoader,
ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider,
ApplicationContext applicationContext) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.typeHandlers = typeHandlersProvider.getIfAvailable();
this.languageDrivers = languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable();
this.applicationContext = applicationContext;
}
当然,我们也可以使用 ObjectProvider#getIfAvailable()
来实现类似 required=false
的功能,这是 @Lazy 提供不了能力。
比如:
@Service
public class B {
@Autowired
private ObjectProvider<A> aProvider;
public void doBiz(){
A a = this.aProvider.getIfAvailable();
if (a == null) {
log.info("a is null");
return;
}
a.m1();
}
}
@Lazy 只能延迟注入,但如果容器中没有这个 bean 的话,在使用时,是会报错的。
所以,ObjectFactory 提供的功能是同 @Lazy 等价的。而 ObjectProvider 可以额外提供 required=false 的能力
小结
ObjectProvider 和 ObjectFactory 都可以提供类似 @Lazy 延迟注入的功能。
另外,ObjectProvider#getIfAvailable()
还可以提供类似 required=false 的功能。