介绍
首先明确一下什么是延迟查找,一般来说通过@Autowired
注解注入一个具体对象的方式是属于实时依赖查找,注入的前提是要保证对象已经被创建。而使用延迟查找的方式是我可以不注入对象的本身,而是通过注入一个代理对象,在需要用到的地方再去取其中真实的对象来使用 ,ObjectFactory
提供的就是这样一种能力。
先来看一下ObjectFactory
和ObjectProvider
的源码
@FunctionalInterface
public interface ObjectFactory {
T getObject() throws BeansException;
}
public interface ObjectProvider extends ObjectFactory, Iterable {
T getObject(Object... args) throws BeansException;
@Nullable
T getIfAvailable() throws BeansException;
default T getIfAvailable(Supplier defaultSupplier) throws BeansException {
T dependency = getIfAvailable();
return (dependency != null ? dependency : defaultSupplier.get());
}
default void ifAvailable(Consumer dependencyConsumer) throws BeansException {
T dependency = getIfAvailable();
if (dependency != null) {
dependencyConsumer.accept(dependency);
}
}
@Nullable
T getIfUnique() throws BeansException;
default T getIfUnique(Supplier defaultSupplier) throws BeansException {
T dependency = getIfUnique();
return (dependency != null ? dependency : defaultSupplier.get());
}
default void ifUnique(Consumer dependencyConsumer) throws BeansException {
T dependency = getIfUnique();
if (dependency != null) {
dependencyConsumer.accept(dependency);
}
}
@Override
default Iterator iterator() {
return stream().iterator();
}
default Stream stream() {
throw new UnsupportedOperationException("Multi element access not supported");
}
default Stream orderedStream() {
throw new UnsupportedOperationException("Ordered element access not supported");
}
}
通过源码可以看出ObjectFactory
是一个顶层接口,内部只提供了直接获取对象的功能,如果对象在容器中不存则直接抛出NoSuchBeanDefinitionException
异常。ObjectProvider
提供了更强大的功能,支持迭代,stream 流等特性,通过getIfAvailable
方法还可以避免NoSuchBeanDefinitionException
异常
用法演示
下面通过代码来演示ObjectFactory
和ObjectProvider
的使用方式
public class ObjectFactoryLazyLookupDemo {
// DefaultListableBeanFactory$DependencyObjectProvider
@Autowired
private ObjectFactory objectFactory;
// DefaultListableBeanFactory$DependencyObjectProvider
@Autowired
private ObjectProvider objectProvider;
public static void main(String[] args) {
// 创建应用上下文
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册当前类为配置类
applicationContext.register(ObjectFactoryLazyLookupDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 获取当前类的实例
ObjectFactoryLazyLookupDemo lazyLookupDemo = applicationContext.getBean(ObjectFactoryLazyLookupDemo.class);
// 获取通过依赖注入的ObjectFactory和ObjectProvider对象
ObjectFactory objectFactory = lazyLookupDemo.objectFactory;
ObjectProvider objectProvider = lazyLookupDemo.objectProvider;
// true
System.out.println(objectFactory.getClass() == objectProvider.getClass());
// true
System.out.println(objectFactory.getObject() == objectProvider.getObject());
// User{id=1, name='lazy lookup'}
System.out.println(objectFactory.getObject());
}
@Bean
private User user() {
User user = new User();
user.setId(1L);
user.setName("lazy lookup");
return user;
}
}
在上述代码中,创建了一个User
对象,在注入的时候并没有直接注入对象本身,而是分别了注入了ObjectFactory
和ObjectProvider
对象,在真正使用时才通过objectFactory.getObject()
去获取真实对象,在注入ObjectFactory
和ObjectProvider
时并没有触发依赖查找的动作,这种方式就是典型的延迟依赖查找。通过两种方式获取的User
对象也是同一个对象
底层原理
在DefaultListableBeanFactory
中有一个resolveDependency(DependencyDescriptor, String, Set
方法,通过名称可以看出此方法专门用来解析依赖。在框架内部处理@Autowired
注解时会调用此方法,方法内部会通过依赖查找的方式查出需要进行依赖注入的Bean。源码如下
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
// 处理Optional类型的依赖注入
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
// 处理ObjectFactory和ObjectProvider类型
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
// 处理JSR330 相关的依赖注入
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
// 查找具体的依赖注入对象
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
在代码中可以看出,如果需要进行依赖注入的Bean类型为ObjectFactory
或者ObjectProvider
,则直接创建一个类型为DependencyObjectProvider
的实例返回。如果注入的是具体类型则代码会走最后的else分支,doResolveDependency()
方法本质上就是通过依赖查找的方式去获取对应的Bean
DefaultListableBeanFactory
的一个内部类,结构如下
private interface BeanObjectProvider extends ObjectProvider, Serializable {
}
private class DependencyObjectProvider implements BeanObjectProvider
通过代码可以看出DependencyObjectProvider
实际上就是ObjectProvider
类型,这里我只保留其getObject()
方法,通过该方法可以看出,只有当使用者调用ObjectProvider#getObject()
方法时,才会通过依赖查找的方式获取对应的Bean
总结和使用场景
通过示例代码和源码分析可以更确定延迟的概念,所谓延迟依赖查找就是等真正用到对象的时候才去获取对象。
那么使用延迟查找的应用场景有哪些呢
可以让依赖的资源充分等到初始化完成之后再使用
-
可以和
@Lazy
注解配合充分实现延迟初始化在本例的代码中,我们只在
user()
方法上面简单标注了@Bean
注解,还可以通过标注@Lazy
注解实现User
对象的延迟初始化,和ObjectFactory
配合使用就可以实现真正用到该对象的那一刻才进行初始化操作。 可用于解决构造器级别的循环依赖