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


    介绍

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

    先来看一下ObjectFactoryObjectProvider的源码

    @FunctionalInterface
    public interface ObjectFactory<T> {
        T getObject() throws BeansException;
    }
    
    • 1
    • 2
    • 3
    • 4
    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");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    通过源码可以看出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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    在上述代码中,创建了一个User对象,在注入的时候并没有直接注入对象本身,而是分别了注入了ObjectFactoryObjectProvider对象,在真正使用时才通过objectFactory.getObject()去获取真实对象,在注入ObjectFactoryObjectProvider时并没有触发依赖查找的动作,这种方式就是典型的延迟依赖查找。通过两种方式获取的User对象也是同一个对象

    底层原理

    DefaultListableBeanFactory中有一个resolveDependency(DependencyDescriptor, String, Set, 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;
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在代码中可以看出,如果需要进行依赖注入的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;
                }
            }
    
            // 省略其他方法.....
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

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

    总结和使用场景

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

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

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

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

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

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

  • 相关阅读:
    AEB前装搭载率逼近50%,沃尔沃「标配」激光雷达释放信号
    网申线上测评,要不要找人代做在线测评?
    字典树 (Trie)
    java性能测试
    C++下基于竞拍算法解决无人机任务分配问题
    Github标星51K!阿里P8熬了700个小时,终于肝出32W字Java面试手册
    Java 格式化之使用 %02X 格式化mac地址
    如何将项目部署到服务器上(全套教程)
    信息学奥赛一本通:1139:整理药名
    【指针内功修炼】函数指针 + 函数指针数组 + 回调函数(二)
  • 原文地址:https://blog.csdn.net/agonie201218/article/details/130725271