5

【Spring源码三千问】@Lazy的替代者ObjectFactory 和 ObjectProvider

 2 years ago
source link: https://blog.51cto.com/laowang6/5558621
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

别再使用 @Lazy 了,试试 ObjectFactory 和 ObjectProvider

@Lazy 可以解决某些特殊场景下的循环依赖问题。
在翻阅 @Lazy 的源码注释时,得知 @Lazy 可以通过 ObjectFactory 和 ObjectProvider 来进行替代。
下面我们就来看看 ObjectFactory 和 ObjectProvider 的原理和使用吧…

@Lazy 相关的知识:
 @Lazy为什么可以解决特殊的循环依赖问题
 @Lazy延迟加载与延迟注入有什么区别

Spring 版本

Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

ObjectProvider 作用分析

ObjectProvider 的类图如下:

【Spring源码三千问】@Lazy的替代者ObjectFactory 和 ObjectProvider_java

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

Spring 对 ObjectProvider 依赖的处理

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

【Spring源码三千问】@Lazy的替代者ObjectFactory 和 ObjectProvider_spring_02

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK