Spring 高级依赖注入 —— Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider
介绍
首先明确一下什么是延迟查找,一般来说通过@Autowired
注解注入一个具体对象的方式是属于实时依赖查找,注入的前提是要保证对象已经被创建。而使用延迟查找的方式是我可以不注入对象的本身,而是通过注入一个代理对象,在需要用到的地方再去取其中真实的对象来使用 ,ObjectFactory
提供的就是这样一种能力。
先来看一下ObjectFactory
和ObjectProvider
的源码
@FunctionalInterface
public interface ObjectFactory<T> {T getObject() throws BeansException;
}
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {T getObject(Object... args) throws BeansException;@NullableT 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);}}@NullableT 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);}}@Overridedefault 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
异常
用法演示
下面通过代码来演示ObjectFactory
和ObjectProvider
的使用方式
public class ObjectFactoryLazyLookupDemo {// DefaultListableBeanFactory$DependencyObjectProvider@Autowiredprivate ObjectFactory<User> objectFactory;// DefaultListableBeanFactory$DependencyObjectProvider@Autowiredprivate 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;// trueSystem.out.println(objectFactory.getClass() == objectProvider.getClass());// trueSystem.out.println(objectFactory.getObject() == objectProvider.getObject());// User{id=1, name='lazy lookup'}System.out.println(objectFactory.getObject());}@Beanprivate User user() {User user = new User();user.setId(1L);user.setName("lazy lookup");return user;}
}
在上述代码中,创建了一个User
对象,在注入的时候并没有直接注入对象本身,而是分别了注入了ObjectFactory<User>
和ObjectProvider<User>
对象,在真正使用时才通过objectFactory.getObject()
去获取真实对象,在注入ObjectFactory
和ObjectProvider
时并没有触发依赖查找的动作,这种方式就是典型的延迟依赖查找。通过两种方式获取的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;@Nullableprivate final String beanName;public DependencyObjectProvider(DependencyDescriptor descriptor, @Nullable String beanName) {// 需要注入对象的类型描述,在本例中即User类型this.descriptor = new NestedDependencyDescriptor(descriptor);// 是否是Optional类型this.optional = (this.descriptor.getDependencyType() == Optional.class);// 被依赖注入的对象,本例中为objectFactoryLazyLookupDemothis.beanName = beanName;}@Overridepublic Object getObject() throws BeansException {if (this.optional) {return createOptionalDependency(this.descriptor, this.beanName);}else {// 内部实际上就是通过依赖查找的方式查出所需的BeanObject 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
配合使用就可以实现真正用到该对象的那一刻才进行初始化操作。 -
可用于解决构造器级别的循环依赖
相关文章:
Spring 高级依赖注入 —— Bean的延迟依赖查找功能,ObjectFactory 和 ObjectProvider
介绍 首先明确一下什么是延迟查找,一般来说通过Autowired注解注入一个具体对象的方式是属于实时依赖查找,注入的前提是要保证对象已经被创建。而使用延迟查找的方式是我可以不注入对象的本身,而是通过注入一个代理对象,在需要用到…...
VSCode--Config
1. basic 1.1 调整字体 1.2 调整 remote login 输入框都在 TERMINAL 中实现 1.3 界面设置成中文 安装插件: 然后配置即可。 2.Linux 2.1 Install 2.1.1 offline Install vscode server 问题描述 内网开发,vscode 自身通过代理安装完 remote 插件后…...
代码随想录刷题第48天|LeetCode198打家劫舍、LeetCode213打家劫舍II、LeetCode337打家劫舍III
1、LeetCode198打家劫舍 题目链接:198、打家劫舍 1、dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。 2、递推公式: 如果偷第i房间,那么dp[i] dp[i - 2] nums[i] …...
C# NTS 获取MuliiLineString中的所有线
/// <summary>/// 获取多段线的所有线/// </summary>/// <param name"ml"></param>/// <returns></returns>public static List<LineString> GetLineStrings(this MultiLineString ml){List<LineString> lineString…...
CodeWhisperer插件使用体验
官方教程点击跳转 使用工具 1.vscode 2.插件(AWS Toolkit),免费使用 安装以后如何使用 1.首先要有一个aws账号 2.插件下载好以后登录aws账号,我们主要用这款插件的CodeWhisperer这个功能,其它的自行看官方教程了解。 注意事项:我们在从vs…...
机器学习笔记 - 多实例学习(MIL)弱监督学习
一、多实例学习概述 多实例学习(MIL)是一种弱监督学习形式,其中训练实例被排列在称为袋的集合中,并为整个袋提供标签。这种方式越来越受到人们的关注,因为它自然适合各种问题,并允许利用弱标记数据。因此,它被应用于计算机视觉和文档分类等不同的应用领域。 多实例学习(…...
SQL Server 2008 定时自动备份和自动删除方法
SQL Server 2008 数据定时自动备份和自动删除方法,同一个计划兼备数据备份数数据删除的操作方法 工具/原料 SQL Server 2008 方法/步骤 1、 点击实例名下的【管理】-【维护计划】-点击鼠标右键,点击【维护计划向导】,填写计划名称&…...
代码生成器实现
代码生成器实现 实现封装元数据的工具类实现代码生成器的代码编写掌握模板创建的 构造数据模型 需求分析 借助Freemarker机制可以方便的根据模板生成文件,同时也是组成代码生成器的核心部分。对于Freemarker而 言,其强调 数据模型 模板 文件 的思…...
【Python基础】Python函数(基本函数)
文章目录 Python函数1、函数多返回值2、函数多种传参方式(1)位置参数(2)关键字参数(3)缺省参数(4)不定长参数位置传递关键字传递 3、小结 Python函数 1、函数多返回值 Q:如果一个函数要有多个返回值,该如何书写代码? # 使用多个变量&#…...
Vue3 + TS + Vite —— 大屏可视化 项目实战
前期回顾 Vue3 Ts Vite pnpm 项目中集成 —— eslint 、prettier、stylelint、husky、commitizen_彩色之外的博客-CSDN博客搭建VIte Ts Vue3项目并集成eslint 、prettier、stylelint、huskyhttps://blog.csdn.net/m0_57904695/article/details/129950163?spm1001.2014…...
EasyExcel 批量导入并校验数据
文章目录 前言一、pom二、使用步骤1.导入对象2.读入数据并保存 前言 EasyExcel 批量导入并校验数据 一、pom <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version></depend…...
亚马逊、Allegro卖家建立属于自己的测评系统,实现批量优质账号养成
卖家搭建一套完整的测评系统,卖家自己能够养出批量优质账号,并完全掌控真实买家的浏览、加购、下单和评价等风控数据规律。我们的系统能够自主加速推广,防御反击,同时节省运营成本,实现高效的测评运营。 我们的系统支…...
springboot的目录结构作用
springboot单体项目结构大概如下。 代码都在src/main下, java是后端代码 java下最基本的包 dao(mapper) entity(model) service controller 其他的包根据项目需求扩展。 resources下是配置文件。 如果不是前后端分离,resources下放的是静态文件…...
微信小程序基础使用-请求数据并渲染
小程序基本使用-请求数据并渲染 小程序模板语法-数据绑定 在js中定义数据 Page({data: {isOpen: true,message: hello world!} })小程序的data是一个对象,不同于vue的data是一个函数 在模块中获取使用数据 小程序中使用 {{}} 实现数据与模板的绑定 内容绑定&a…...
代码随想录训练营Day55| 392.判断子序列 ;115.不同的子序列
392.判断子序列 class Solution {public boolean isSubsequence(String s, String t) {int m s.length();int n t.length();if(m>n) return false;int[][] dp new int[m1][n1];dp[0][0]0;for(int i1;i<m;i){for(int j1;j<n;j){if(s.charAt(i-1)t.charAt(j-1)){dp[i…...
网络作业9【计算机网络】
网络作业9【计算机网络】 前言推荐网络作业9一. 单选题(共12题,36分)二. 多选题(共1题,3分)三. 填空题(共2题,10分)四. 阅读理解(共1题,17分&…...
C++ QT 上传图片至mysql数据库
以下是一个简单的C QT上传图片至MySQL数据库的代码示例: #include <QtSql> #include <QFile> #include <QByteArray> int main() { //连接数据库 QSqlDatabase db QSqlDatabase::addDatabase("QMYSQL"); …...
2023去水印小程序saas系统源码修复独立版v1.0.3+uniapp前端
🎈 限时活动领体验会员:可下载程序网创项目短视频素材 🎈 🎉 有需要的朋友记得关赞评,阅读文章底部来交流!!! 🎉 ✨ 源码介绍 一个基于uniapp写的小程序,后端…...
【ChatGPT】数据科学 ChatGPT Cheat Sheet 书籍分享(阿里云盘下载)
封皮 以下为书中部分内容的机器翻译 我们的重要提示指南 1. 以 AI 角色的描述开始提示。 例如,“你是{x}”或“我希望你扮演{x}”。如果您不确定,请尝试“你是一个有帮助的助手”。 例如,您是 OpenAI 的数据科学家,您正在研究大型…...
使用 Docker-compose 搭建lnmp
服务编排: 应用编排: 单机环境下:shell/python脚本多机/集群环境下:ansible、saltstack、pubbet docker容器编排: 单机:docker-compose多机/集群:docker swarm,mesos marathon&a…...
chatgpt赋能python:Python中的矩阵合并方法:介绍和使用方法
Python中的矩阵合并方法: 介绍和使用方法 矩阵合并是Python编程中常用的操作之一,特别是针对数据分析、机器学习和深度学习等领域。Python提供了多种方法来合并矩阵,本文将介绍这些方法并分享如何在实际应用中使用它们。 普通矩阵合并 最基础的矩阵合…...
Java动态代理:优化静态代理模式的灵活解决方案
文章目录 代理模式定义具体实现分析优缺点 优化使用动态代理解决优化相关知识动态代理种类场景应用 代理模式 定义 代理模式,为其他对象提供一种代理以控制对这个对象的访问 具体实现 代理模式的具体实现描述可以分为以下几个步骤: 创建抽象对象接…...
四种Bootloader程序安全机制设计
正文 大家周末好,我是bug菌~ 不管是玩单片机还是嵌入式linux,基本上都会接触到bootloader,所以bootloader程序也是一个关键的组件,进行硬件初始化,应用程序的合法性、完成性检测、升级功能等等都与其息息相关。 像一些…...
【DBA 警世录之习惯性命令---读书笔记】
👈【上一篇】 💖The Begin💖点点关注,收藏不迷路💖 【下一篇】👉 🔻【💣 话题引入:既然 DBA 这个职业如此危险,那么哪些习惯是 DBA 必须养成的呢&#x…...
Vue中如何进行状态持久化(LocalStorage、SessionStorage)
Vue中如何进行状态持久化(LocalStorage、SessionStorage)? 在Vue应用中,通常需要将一些状态进行持久化,以便在用户关闭浏览器或刷新页面后,仍能保留之前的状态。常见的持久化方式包括LocalStorage和Sessio…...
【30天熟悉Go语言】6 Go 复杂数据类型之指针
文章目录 一、前言二、数据类型总览三、指针1、特殊运算符& *2、内存角度来看指针3、使用指针修改数据4、指针使用的注意事项5、对比着看Java的引用类型 三、总结 一、前言 Go系列文章: GO开篇:手握Java走进Golang的世界2 Go开发环境搭建、Hello Wor…...
Linux内核使用红黑树的场景
进程调度队列 (Process Scheduling):内核需要对进程按照一定的调度策略进行排队,以便更好地利用 CPU 的时间片。因此,内核使用红黑树作为查找和管理进程调度队列的数据结构,以支持快速的查找、插入和删除操作。 文件系统 (File S…...
遗留的 AppSec 工具迷失在云端
随着应用程序开发步伐的加快,IT 和安全团队正在对旧的应用程序安全(AppSec) 工具失去信心。 根据 Backslash 对 300 名 CISO、AppSec 经理和工程师的调查,遗留工具无法跟上并陷入永远的追赶游戏。 影响是深远的,大多数组织都看到云原生 App…...
直流稳压电源与信号产生电路(模电速成)
目录 一、直流稳压电源 1、直流稳压电路 2、串联型稳压电路 3、集成稳压电路 二、信号产生电路 1、振荡电路 2、波形发生器 一、直流稳压电源 1、直流稳压电路 直流电源由 变压器、整流、滤波、稳压 四部分组成 整流:将交流变为直流 滤波:减小…...
0202性能分析-索引-MySQL
1 索引语法 创建索引 CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name(index_column_name,...);Index_name:规范为idx_表名_字段名... 查看索引 SHOW INDEX FROM table_name;删除索引 DROP INDEX index_name ON table_name;按照下列要求,创建…...
做网站注册的商标类别/深圳网络推广案例
2019独角兽企业重金招聘Python工程师标准>>> 你英语四级过了吗?功夫熊猫2看了吗?去除停用词后,功夫熊猫2使用了816个英语单词,你会说很简单吧,别急,我给你分析一下,这些单词中有125个…...
wordpress媒体保存目录/百度手机助手app下载并安装
一、最简单的枚举 public enum Season{ Spring,Summer,Fall,Winter}; 二、带构造器的枚举 如下:EOrderType就是枚举的构造函数 例如NormalOrder(0, “一般订单”) 第一个0对于构造函数的type,第二个参数对应构造函数的desc getOrderType 注意&…...
wordpress 列表页模板/太原搜索引擎优化
步骤一:打开下面这个地址:http://api.map.baidu.com/lbsapi/creatmap/index.html 步骤二:定位中心点 在打开的页面左侧,输入企业的详细地址,然后点查找,即可在右侧的地图中显示企业的具体位置。 …...
浏览器做单页网站项目/百度排名优化工具
安装 Linux 主机时,如果选择 最小化安装! 配置 vnc 远程桌面可以参考:Linux 配置 VNC 远程桌面 使用 vnc 等工具连接通常显示如下: 也就是无法使用图形化界面,可以通过 yum 直接安装图形化界面: Linux 6: yum groupinstall -y "X Window System" yum gr…...
哪个行业最容易做网站/哪些平台可以免费打广告
以下信件于2月7日由Provost Martin A. Schmidt发送给麻省理工学院社区。对麻省理工学院社区的成员:2018年10月,麻省理工学院宣布成立麻省理工学院斯蒂芬A.施瓦茨曼计算学院。该学院旨在建立一个共享的学术结构,以促进计算奖学金和资源与麻省理…...
html做的旅游网站/百度最新秒收录方法2023
在macos里ssh的秘钥放在~/.ssh文件夹中: id_rsa.pub就是秘钥所在的文件,这个文件是通过这一串命令生成的: ssh-keygen -t -C "你的GitHub邮箱" 这一串执行后会有三个提示,连环回车就行了都不用填,如果不是第一次生成会提示是否覆盖,覆盖就完事儿了. 生成后将文件中…...