Spring框架-ObjectProvider更加宽泛的依赖注入

简介: 从上面的过程中我们可以看出,但Spring中某个Bean的依赖类型为ObjectProvider时,我们不需要提供一个ObjectProvider类型的Bean到容器中,只需要提供一个T类型的Bean到容器中,容器会自动将其包装成一个ObjectProvider,然后注入到依赖中

ObjectProvider

首先浅说一下Spring的依赖注入 , 在使用Spring的过程中 , 我们有多种形式将一个类注入到另一个类中 , 比如@Autowared,@Resource等等 , 其中@Autowared有多种使用场景 , 比如


* 注解在构造函数

@Service
public class OrderServiceImpl implements OrderService {
    private UserService userService;
    @Autowired
    public OrderServiceImpl(UserService userService) {
        this.userService = userService;
    }
}

* 注解在属性

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private UserService userService;
}

* 注解在方法上


@Service
public class OrderServiceImpl implements OrderService {
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

但在spring 4.3之后,引入了一个新特性:当构造方法的参数为单个构造参数时,可以不使用@Autowired进行注解。于是上面的代码就变成这样

@Service
public class OrderServiceImpl implements OrderService {
    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

没有使用@Autowared , 因为Spring会把SetUserService()解析 , 解析为userService , 然后当作属性 , 同样是在Spring4.3版本中,不仅隐式的注入了单构造参数的属性。还引入了ObjectProvider接口 , 它继承自ObjectFactory


f35f9079466b6fb13e15d08c3863fbdd_watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbGluZ2VyaW5nIGZlYXI=,size_15,color_FFFFFF,t_70,g_se,x_16.png


专门为注入点设计, 可以让注入变得的更加宽松 , 那么在什么时候使用ObjectProvider呢?


如果注入实例为空 , 则使用ObjectProvider可以避免强依赖导致的依赖不存在问题

如果有多个实例 , ObjectProvider方法会根据实现的Ordered接口或@Ordered来顺序 的寻找Bean对象

Spring5.1之后提供了Stream的orderedStream()方法来得到有序的Stream , 如果使用ObjectProvider ,上面的代码变为


@Service
public class OrderServiceImpl implements OrderService {
    private UserService userService;
    public void setUserService(ObjectProvider<UserService> userService) {
        this.userService = userService.getIfAvailable();
    }
}

解决的问题:


容器中没有Bean时,抛出Parameter 0 of constructor in com.example.demo.FooServicerequired a bean of type 'com.example.demo.FooRepository' that could not be found.。


或者这样

@Service
public class OrderServiceImpl implements OrderService {
    private UserService userService;
    public void setUserService(ObjectProvider<UserService> userService) {
        this.userService = userService.orderedStream().findFirst().orElse(null);
    }
}

这样的好处很明显 , 如果容器中不存在userService或者有多个时 , 可以从容的处理 , 但是 , 如果userService必须不为空 , 那么这个异常就从启动阶段转移到业务运行阶段


解决的问题:当容器存在多个Bean,我们可以调用它的流式方法获取一个自己想要的依赖


在Spring4.3之前 , 当我们的构造函数想依赖于其他的Bean时 , 需要使用@Autowared(这里不考虑自动注入) ,而在4.3之后很显然不用这样做 ,我么只需要提供一个构造函数 , 并且构造函数所需要的Bean在Spring容器中


实际上官网中也指出,如果依赖关系是强制的,那么最好使用构造函数进行注入


官方推荐理由


单一职责: 当使用构造函数注入的时候,你会很容易发现参数是否过多,这个时候需要考虑你这个类的职责是否过大,考虑拆分的问题;而当使用@Autowired注入field的时候,不容易发现问题


依赖不可变: 只有使用构造函数注入才能注入final


依赖隐藏:使用依赖注入容器意味着类不再对依赖对象负责,获取依赖对象的职责就从类抽离出来,IOC容器会帮你自动装备。这意味着它应该使用更明确清晰的公用接口方法或者构造器,这种方式就能很清晰的知道类需要什么和到底是使用setter还是构造器


降低容器耦合度: 依赖注入框架的核心思想之一是托管类不应依赖于所使用的DI容器。换句话说,它应该只是一个普通的POJO,只要您将其传递给所有必需的依赖项,就可以独立地实例化。这样,您可以在单元测试中实例化它,而无需启动IOC容器并单独进行测试(使用一个可以进行集成测试的容器)。如果没有容器耦合,则可以将该类用作托管或非托管类,甚至可以切换到新的DI框架。


那么不需要精确的指定@Autowared , 这无疑简化了我们的开发 , 但是同时也带来了问题 , 就比如上面的例子 , 如果Spring容器不存在或者存在多个Bean是会报错的 ,那么为了解决这个问题 , ObejectProvider出场了 , 使用ObjectProvider则避免了强依赖导致的依赖对象不存在异常;如果有多个实例,ObjectProvider的方法可以根据Bean实现的Ordered接口或@Order注解指定的先后顺序获取一个Bean ,主要在org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency()


方法中使用


@Override
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, 
                                Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    // descriptor代表当前需要注入的那个字段,或者方法的参数,也就是注入点
    // ParameterNameDiscovery用于解析方法参数名称
    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    // 1. Optional<T>
    if (Optional.class == descriptor.getDependencyType()) {
        return createOptionalDependency(descriptor, requestingBeanName);
        // 2. ObjectFactory<T>、ObjectProvider<T>
    } else if (ObjectFactory.class == descriptor.getDependencyType() ||
            ObjectProvider.class == descriptor.getDependencyType()) {
        return new DependencyObjectProvider(descriptor, requestingBeanName);
        // 3. javax.inject.Provider<T>
    } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
        return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    } else {
        // 4. @Lazy
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                descriptor, requestingBeanName);
        // 5. 正常情况
        if (result == null) {
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}


他其实都会调用doResolveDependency()方法,我们着重关注上面的第二种情况,可以看到当注入点为ObjectFactory或者ObjectProvider时,会new一个DependencyObjectProvider返回出去,那么返回的这个DependencyObjectProvider是什么呢?看看继承关系

55369d33a84d1103d72b4ba244be8d64_watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbGluZ2VyaW5nIGZlYXI=,size_17,color_FFFFFF,t_70,g_se,x_16.png

然后看看内部方法getIfAvailable();


public Object getIfAvailable() throws BeansException {
    // 用于解决嵌套的情况,像这种:ObjectProvider<Optional<T>>
    if (this.optional) {
        return createOptionalDependency(this.descriptor, this.beanName);
    }
    else {
        DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {
            @Override
            public boolean isRequired() {
                return false;
            }
        };
        // 最终还是会调用这个方法解决依赖
        return doResolveDependency(descriptorToUse, this.beanName, null, null);
    }
}

从上面的过程中我们可以看出,但Spring中某个Bean的依赖类型为ObjectProvider时,我们不需要提供一个ObjectProvider类型的Bean到容器中,只需要提供一个T类型的Bean到容器中,容器会自动将其包装成一个ObjectProvider,然后注入到依赖中

目录
打赏
0
0
0
0
9
分享
相关文章
Spring框架初识
Spring 是一个分层的轻量级开源框架,核心功能包括控制反转(IOC)和面向切面编程(AOP)。主要模块有核心容器、Spring 上下文、AOP、DAO、ORM、Web 模块和 MVC 框架。它通过 IOC 将配置与代码分离,简化开发;AOP 提供了声明性事务管理等增强功能。
54 21
Spring框架初识
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
118 29
通过springboot框架创建对象(一)
在Spring Boot中,对象创建依赖于Spring框架的核心特性——控制反转(IoC)和依赖注入(DI)。IoC将对象的创建和管理交由Spring应用上下文负责,开发者只需定义依赖关系。DI通过构造函数、setter方法或字段注入实现依赖对象的传递。Spring Boot的自动配置机制基于类路径和配置文件,自动为应用程序配置Spring容器,简化开发过程。Bean的生命周期包括定义扫描、实例化、依赖注入、初始化和销毁回调,均由Spring容器管理。这些特性提高了开发效率并简化了代码维护。
Spring Boot中的日志框架选择
在Spring Boot开发中,日志管理至关重要。常见的日志框架有Logback、Log4j2、Java Util Logging和Slf4j。选择合适的日志框架需考虑性能、灵活性、社区支持及集成配置。本文以Logback为例,演示了如何记录不同级别的日志消息,并强调合理配置日志框架对提升系统可靠性和开发效率的重要性。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
理解和解决Spring框架中的事务自调用问题
事务自调用问题是由于 Spring AOP 代理机制引起的,当方法在同一个类内部自调用时,事务注解将失效。通过使用代理对象调用、将事务逻辑分离到不同类中或使用 AspectJ 模式,可以有效解决这一问题。理解和解决这一问题,对于保证 Spring 应用中的事务管理正确性至关重要。掌握这些技巧,可以提高开发效率和代码的健壮性。
145 13
互联网应用主流框架整合之Spring Boot开发
通过本文的介绍,我们详细探讨了Spring Boot开发的核心概念和实践方法,包括项目结构、数据访问层、服务层、控制层、配置管理、单元测试以及部署与运行。Spring Boot通过简化配置和强大的生态系统,使得互联网应用的开发更加高效和可靠。希望本文能够帮助开发者快速掌握Spring Boot,并在实际项目中灵活应用。
82 5
Spring框架中的事件机制:深入理解与实践
Spring框架是一个广泛使用的Java企业级应用框架,提供了依赖注入、面向切面编程(AOP)、事务管理、Web应用程序开发等一系列功能。在Spring框架中,事件机制是一种重要的通信方式,它允许不同组件之间进行松耦合的通信,提高了应用程序的可维护性和可扩展性。本文将深入探讨Spring框架中的事件机制,包括不同类型的事件、底层原理、应用实践以及优缺点。
92 8
轻松掌握Spring依赖注入:打造你的登录验证系统
本文以轻松活泼的风格,带领读者走进Spring框架中的依赖注入和登录验证的世界。通过详细的步骤和代码示例,我们从DAO层的创建到Service层的实现,再到Spring配置文件的编写,最后通过测试类验证功能,一步步构建了一个简单的登录验证系统。文章不仅提供了实用的技术指导,还以口语化和生动的语言,让学习变得不再枯燥。
56 2
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等