(别再使用 @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 提供的是通过 ObjectProvider#getObject() 或者 ObjectProvider#getIfAvailable() 获取 Object (即: bean) 的能力。
也就是说,在依赖注入时,注入的是一个 ObjectProvider 对象,想要获取到真正的 bean 时,可以通过调用 ObjectProvider#getObject() 或者 ObjectProvider#getIfAvailable()
所以,它跟 @Lazy 起到的作用是很相似的。

Spring 对 ObjectProvider 依赖的处理

Spring 在 populateBean 时,会处理依赖属性的注入,最终会调用 DefaultListableBeanFactory#resolveDependency() 来对依赖进行解析。
相关的源码如下:
resolveDependency2

可以看到,如果是 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 的功能。