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,然后注入到依赖中

相关文章
|
22天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
1月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
36 0
|
26天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
43 1
Spring 框架:Java 开发者的春天
|
18天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
36 2
|
18天前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
70 1
|
26天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
1月前
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
近期,阿里云重磅发布了首款面向 Java 开发者的开源 AI 应用开发框架:Spring AI Alibaba(项目 Github 仓库地址:alibaba/spring-ai-alibaba),Spring AI Alibaba 项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。本文将详细介绍 Spring AI Alibaba 的核心特性,并通过「智能机票助手」的示例直观的展示 Spring AI Alibaba 开发 AI 应用的便利性。示例源
|
1月前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。
|
1月前
|
Java 数据库连接 数据库
让星星⭐月亮告诉你,SSH框架01、Spring概述
Spring是一个轻量级的Java开发框架,旨在简化企业级应用开发。它通过IoC(控制反转)和DI(依赖注入)降低组件间的耦合度,支持AOP(面向切面编程),简化事务管理和数据库操作,并能与多种第三方框架无缝集成,提供灵活的Web层支持,是开发高性能应用的理想选择。
36 1