您现在的位置是:首页 >技术交流 >Spring 高级依赖注入 —— Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider网站首页技术交流

Spring 高级依赖注入 —— Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider

Young丶 2024-06-17 10:32:14
简介Spring 高级依赖注入 —— Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider

介绍

首先明确一下什么是延迟查找,一般来说通过@Autowired注解注入一个具体对象的方式是属于实时依赖查找,注入的前提是要保证对象已经被创建。而使用延迟查找的方式是我可以不注入对象的本身,而是通过注入一个代理对象,在需要用到的地方再去取其中真实的对象来使用 ,ObjectFactory提供的就是这样一种能力。

先来看一下ObjectFactoryObjectProvider的源码

@FunctionalInterface
public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {

    T getObject(Object... args) throws BeansException;

    
    @Nullable
    T getIfAvailable() throws BeansException;
    
    default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {
        T dependency = getIfAvailable();
        return (dependency != null ? dependency : defaultSupplier.get());
    }

    default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
        T dependency = getIfAvailable();
        if (dependency != null) {
            dependencyConsumer.accept(dependency);
        }
    }

    @Nullable
    T getIfUnique() throws BeansException;

    
    default T getIfUnique(Supplier<T> defaultSupplier) throws BeansException {
        T dependency = getIfUnique();
        return (dependency != null ? dependency : defaultSupplier.get());
    }

    default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
        T dependency = getIfUnique();
        if (dependency != null) {
            dependencyConsumer.accept(dependency);
        }
    }

    @Override
    default Iterator<T> iterator() {
        return stream().iterator();
    }


    default Stream<T> stream() {
        throw new UnsupportedOperationException("Multi element access not supported");
    }

    
    default Stream<T> orderedStream() {
        throw new UnsupportedOperationException("Ordered element access not supported");
    }

}

通过源码可以看出ObjectFactory是一个顶层接口,内部只提供了直接获取对象的功能,如果对象在容器中不存则直接抛出NoSuchBeanDefinitionException异常。ObjectProvider提供了更强大的功能,支持迭代,stream 流等特性,通过getIfAvailable方法还可以避免NoSuchBeanDefinitionException 异常

用法演示

下面通过代码来演示ObjectFactoryObjectProvider的使用方式

public class ObjectFactoryLazyLookupDemo {

    // DefaultListableBeanFactory$DependencyObjectProvider
    @Autowired
    private ObjectFactory<User> objectFactory;
    
    // DefaultListableBeanFactory$DependencyObjectProvider
    @Autowired
    private ObjectProvider<User> 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<User> objectFactory = lazyLookupDemo.objectFactory;
        ObjectProvider<User> 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<User>ObjectProvider<User>对象,在真正使用时才通过objectFactory.getObject()去获取真实对象,在注入ObjectFactoryObjectProvider时并没有触发依赖查找的动作,这种方式就是典型的延迟依赖查找。通过两种方式获取的User对象也是同一个对象

底层原理

DefaultListableBeanFactory中有一个resolveDependency(DependencyDescriptor, String, Set<String>, TypeConverter) 方法,通过名称可以看出此方法专门用来解析依赖。在框架内部处理@Autowired注解时会调用此方法,方法内部会通过依赖查找的方式查出需要进行依赖注入的Bean。源码如下

    public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
            @Nullable Set<String> 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<T> extends ObjectProvider<T>, Serializable {
}

private class DependencyObjectProvider implements BeanObjectProvider<Object> {

        private final DependencyDescriptor descriptor;

        private final boolean optional;

        @Nullable
        private final String beanName;

        public DependencyObjectProvider(DependencyDescriptor descriptor, @Nullable String beanName) {
            // 需要注入对象的类型描述,在本例中即User类型
            this.descriptor = new NestedDependencyDescriptor(descriptor);
            // 是否是Optional类型
            this.optional = (this.descriptor.getDependencyType() == Optional.class);
            // 被依赖注入的对象,本例中为objectFactoryLazyLookupDemo
            this.beanName = beanName;
        }

        @Override
        public Object getObject() throws BeansException {
            if (this.optional) {
                return createOptionalDependency(this.descriptor, this.beanName);
            }
            else {
                // 内部实际上就是通过依赖查找的方式查出所需的Bean
                Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
                if (result == null) {
                    throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
                }
                return result;
            }
        }

        // 省略其他方法.....
    }

通过代码可以看出DependencyObjectProvider实际上就是ObjectProvider类型,这里我只保留其getObject()方法,通过该方法可以看出,只有当使用者调用ObjectProvider#getObject()方法时,才会通过依赖查找的方式获取对应的Bean

总结和使用场景

通过示例代码和源码分析可以更确定延迟的概念,所谓延迟依赖查找就是等真正用到对象的时候才去获取对象。

那么使用延迟查找的应用场景有哪些呢

  • 可以让依赖的资源充分等到初始化完成之后再使用

  • 可以和@Lazy注解配合充分实现延迟初始化

    在本例的代码中,我们只在user()方法上面简单标注了@Bean注解,还可以通过标注@Lazy注解实现User对象的延迟初始化,和ObjectFactory配合使用就可以实现真正用到该对象的那一刻才进行初始化操作。

  • 可用于解决构造器级别的循环依赖

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。