手写Spring:第5章-注入属性和依赖对象
文章目录
- 一、目标:注入属性和依赖对象
- 二、设计:注入属性和依赖对象
- 三、实现:注入属性和依赖对象
- 3.0 引入依赖
- 3.1 工程结构
- 3.2 注入属性和依赖对象类图
- 3.3 定义属性值和属性集合
- 3.3.1 定义属性值
- 3.3.2 定义属性集合
- 3.4 Bean定义补全
- 3.5 Bean属性填充
- 四、测试:注入属性和依赖对象
- 4.1 用户Bean对象
- 4.1.1 用户Dao对象
- 4.1.2 用户Service对象
- 4.2 单元测试
- 五、总结:注入属性和依赖对象
一、目标:注入属性和依赖对象
💡 已经完成 实现一个容器、定义和注册Bean、实例化Bean、按照是否包含构造函数实现不同的实例化策略,那么在创建对象实例化,我们还缺少什么?
- 其实还缺少
类中是否有属性的问题
,如果类中包含属性那么在实例化的时候就需要把属性信息填充上,这样才是一个完整的对象创建。 - 对于属性的填充不只是
int、Long、String
,还包括没有实例化的对象属性,都需要在 Bean 创建时进行填充操作。
二、设计:注入属性和依赖对象
💡 技术设计:注入属性和依赖对象
- 属性填充是在 Bean 使用
newInstance
或者cglib
创建后,开始补全属性信息,那么就可以在类AbstractAutowireCapableBeanFactory
的createBean
方法中添加补全属性方法。
- 属性填充要在类实例化创建之后,也就是需要在
AbstractAutowireCapableBeanFactory#createBean
方法中添加applyPropertyValues
操作。 - 由于需要在创建 Bean 时填充属性操作,那么就需要在 bean 定义 BeanDefinition 类中,添加 PropertyValues 信息。
- 另外是填充属性信息还包括 Bean 的对象类型,也就是需要再定义一个 BeanReference。
- 里面其实就是一个简单的 Bean 名称,再具体的实例化操作时进行递归创建和填充。
- 与 Spring 源码实现一样,Spring 源码中 BeanReference 是一个接口。
三、实现:注入属性和依赖对象
3.0 引入依赖
pom.xml
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.5.0</version>
</dependency>
3.1 工程结构
spring-step-04
|-src|-main| |-java| |-com.lino.springframework| |-factory| | |-config| | | |-BeanDefinition.java| | | |-BeanReference.java| | | |-SingletonBeanRegistry.java| | |-support| | | |-AbstractAutowireCapableBeanFactory.java| | | |-AbstractBeabFactory.java| | | |-BeanDefinitionRegistry.java| | | |-CglibSubclassingInstantiationStrategy.java| | | |-DefaultListableBeanFactory.java| | | |-DefaultSingletonBeanRegistry.java| | | |-InstantiationStrategy.java| | | |-SimpleInstantiationStrategy.java| | |-BeanFactory.java| |-BeansException.java| |-PropertyValue.java| |-PropertyValues.java|-test|-java|-com.lino.springframework.test|-bean| |-UserDao.java| |-UserService.java|-ApiTest.java
3.2 注入属性和依赖对象类图
- 新增加3个类,BeanReference(类引用)、PropertyValue(属性值)、PropertyValues(属性集合),分别用于类和其他类型属性填充操作。
- 另外改动的类主要是 AbstractAutowireCapableBeanFactory,在
createBean
中补全属性填充部分。
3.3 定义属性值和属性集合
3.3.1 定义属性值
PropertyValue.java
package com.lino.springframework;/*** @description: Bean属性信息*/
public class PropertyValue {/*** 属性名称*/private final String name;/*** 属性值*/private final Object value;public PropertyValue(String name, Object value) {this.name = name;this.value = value;}public String getName() {return name;}public Object getValue() {return value;}
}
3.3.2 定义属性集合
PropertyValues.java
package com.lino.springframework;import java.util.ArrayList;
import java.util.List;/*** @description: 属性值集合*/
public class PropertyValues {private final List<PropertyValue> propertyValueList = new ArrayList<>();public void addPropertyValue(PropertyValue pv) {this.propertyValueList.add(pv);}public PropertyValue[] getPropertyValues() {return this.propertyValueList.toArray(new PropertyValue[0]);}public PropertyValue getPropertyValue(String propertyName) {for (PropertyValue pv : this.propertyValueList) {if (pv.getName().equals(propertyName)) {return pv;}}return null;}
}
- 这两个类的作用就是创建出一个用于传递类中属性信息的类,因为属性可能会有很多,所以还需要定义一个集合包装下。
3.4 Bean定义补全
BeanReference.java
package com.lino.springframework.factory.config;/*** @description: Bean 引用*/
public class BeanReference {private final String beanName;public BeanReference(String beanName) {this.beanName = beanName;}public String getBeanName() {return beanName;}
}
- 在 Bean 注册的过程中是需要传递 Bean 的信息。
- 所以为了把属性一定交给 Bean 定义,所以这里填充了 PropertyValues 属性,同时把两个构造函数做了一些简单的优化,避免后面
for
循环时还得判断属性填充是否为空。
3.5 Bean属性填充
AbstractAutowireCapableBeanFactory.java
package com.lino.springframework.factory.support;import cn.hutool.core.bean.BeanUtil;
import com.lino.springframework.BeansException;
import com.lino.springframework.PropertyValue;
import com.lino.springframework.PropertyValues;
import com.lino.springframework.factory.config.BeanDefinition;
import com.lino.springframework.factory.config.BeanReference;
import java.lang.reflect.Constructor;/*** @description: 实现默认bean创建的抽象bean工厂超类*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {Object bean = null;try {bean = createBeanInstance(beanDefinition, beanName, args);// 给bean填充属性applyPropertyValues(beanName, bean, beanDefinition);} catch (Exception e) {throw new BeansException("Instantiation of bean failed", e);}registerSingletonBean(beanName, bean);return bean;}private void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {try {PropertyValues propertyValues = beanDefinition.getPropertyValues();for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {String name = propertyValue.getName();Object value = propertyValue.getValue();if (value instanceof BeanReference) {// A 依赖 B,获取 B 的实例化BeanReference beanReference = (BeanReference) value;value = getBean(beanReference.getBeanName());}// 属性填充BeanUtil.setFieldValue(bean, name, value);}} catch (Exception e) {throw new BeansException("Error setting property values: " + beanName);}}protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {Constructor constructorToUse = null;Class<?> beanClass = beanDefinition.getBeanClass();Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();for (Constructor ctor : declaredConstructors) {if (null != args && ctor.getParameterTypes().length == args.length) {constructorToUse = ctor;break;}}return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);}public InstantiationStrategy getInstantiationStrategy() {return instantiationStrategy;}public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {this.instantiationStrategy = instantiationStrategy;}
}
- 这个类主要包括三个方法:
createBean
、createBeanInstance
、applyPropertyValues
,这里我们主要关注createBean
的方法中调用的applyPropertyValues
方法。 - 在
applyPropertyValues
中,通过获取beanDefinition.getPropertyValues()
循环进行属性填充操作。- 如果遇到的是
BeanReference
,那么就需要递归获取 Bean 实例,调用getBean
方法。
- 如果遇到的是
- 当把依赖的 Bean 对象创建完成后,会递归回现在属性填充中。
四、测试:注入属性和依赖对象
4.1 用户Bean对象
4.1.1 用户Dao对象
UserDao.java
package com.lino.springframework.test.bean;import java.util.HashMap;
import java.util.Map;/*** @description: 模拟用户DAO类*/
public class UserDao {private static Map<String, String> hashMap = new HashMap<>();static {hashMap.put("10001", "张三");hashMap.put("10002", "李四");hashMap.put("10003", "王五");}public String queryUserName(String uId) {return hashMap.get(uId);}
}
4.1.2 用户Service对象
UserServce.java
package com.lino.springframework.test.bean;/*** @description: 模拟用户 Bean 对象*/
public class UserService {private String uId;private UserDao userDao;/*** 查询用户信息*/public void queryUserInfo() {System.out.println("查询用户信息: " + userDao.queryUserName(uId));}public String getuId() {return uId;}public void setuId(String uId) {this.uId = uId;}public UserDao getUserDao() {return userDao;}public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
- 在
UserService
中注入UserDao
,这样就能体现出 Bean 属性的依赖。
4.2 单元测试
ApiTest.java
@Test
public void test_BeanFactory() {// 1.初始化 BeanFactoryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 2.UserDao注册beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));// 3.UserService 设置属性[uId、userDao]PropertyValues propertyValues = new PropertyValues();propertyValues.addPropertyValue(new PropertyValue("uId", "10001"));propertyValues.addPropertyValue(new PropertyValue("userDao", new BeanReference("userDao")));// 4.UserService 注入beanBeanDefinition beanDefinition = new BeanDefinition(UserService.class, propertyValues);beanFactory.registerBeanDefinition("userService", beanDefinition);// 5.获取beanUserService userService = (UserService) beanFactory.getBean("userService");userService.queryUserInfo();
}
- 与直接获取 Bean 对象不同,这次我们还需要先把
userDao
注入到 Bean 容器中。beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class))
。
- 接下来就是属性填充的操作。
- 一种是普通属性:
new PropertyValue("uId", "10001")
。 - 另外一种是对象属性:
new PropertyValue("userDao", new BeanReference("userDao"))
。
- 一种是普通属性:
- 最后是正常获取
userService
对象,调用方法即可。
测试结果
查询用户信息: 张三
- 从测试结果来看,属性填充已经起作用了,因为只有属性填充后,才能调用到 Dao 方法,如:
userDao.queryUserName(uId)
。
五、总结:注入属性和依赖对象
- 本章对 AbstructAutowireCapableBeanFactory 类中的创建对象功能又做了扩充,依赖于是否有构造函数的实例化策略完成后。
- 开始补充 Bean 属性信息。当遇到 Bean 属性为 Bean 对象时,需要递归处理。
- 最后在属性填充时需要用到反射操作,也可以使用一些工具类处理。
相关文章:
手写Spring:第5章-注入属性和依赖对象
文章目录 一、目标:注入属性和依赖对象二、设计:注入属性和依赖对象三、实现:注入属性和依赖对象3.0 引入依赖3.1 工程结构3.2 注入属性和依赖对象类图3.3 定义属性值和属性集合3.3.1 定义属性值3.3.2 定义属性集合 3.4 Bean定义补全3.5 Bean…...
初识集合框架 -Java
目录 一、集合框架的概念 二、集合框架的重要性 三、涉及的数据结构和算法 3.1 什么是数据结构 3.2 集合框架(容器)背后对应的数据结构 3.3 相关的Java知识 3.4 什么是算法 3.5 如何学好数据结构和算法 一、集合框架的概念 Java 集合框架,…...
目标检测笔记(十五): 使用YOLOX完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)
文章目录 一、目标检测介绍二、YOLOX介绍三、源码获取四、环境搭建4.1 环境检测 五、数据集准备六、模型训练七、模型验证八、模型测试 一、目标检测介绍 目标检测(Object Detection)是计算机视觉领域的一项重要技术,旨在识别图像或视频中的…...
深眸科技自研轻辙视觉引擎,以AI机器视觉赋能杆号牌识别与分拣
电线杆号牌作为电力行业标识的一种,相当于电线杆的“身份证”,担负着宣传电力知识、安全警示的作用,用于户外使用标记输电线路电压等级、线路名称、杆塔编号等,能够清晰地记录电力线路杆的信息,并为电力线路的更改以及…...
Shell命令管理进程
Shell命令管理进程 列出进程 ps命令 top命令 管理后台进程 启动后台进程 查看后台进程 jobs和ps的区别 停止进程 Linux除了是一种多用户操作系统之外,还是一种多任务系统。多任务意味着可以同时运行多个程序。Linux 提供了相关的工具来列出运行中的进程,监视…...
python创建exe文件
1、搭建环境 pip install pyinstaller 2、准备测试代码 exe_test.py import timeprint("hello") print("hello") print("hello") print("hello")time.sleep(5) 注:添加sleep以便在执行exe文件的时候能看到结果 3、生…...
【数据结构】AVL树的插入与验证
文章目录 一、基本概念1.发展背景2.性质 二、实现原理①插入操作1.平衡因子1.1平衡因子的更新1.1.1树的高度变化1.1.2树的高度不变 2. 旋转2.1左旋2.2右旋2.3右左双旋2.4 左右双旋 ②验证1.求二叉树高度2. 判断是否为AVL树 源码总结 一、基本概念 1.发展背景 普通的二叉搜索树…...
9.3.3网络原理(网络层IP)
一.报文: 1.4位版本号:IPv4和IPv6(其它可能是实验室版本). 2.4位首部长度:和TCP一样,可变长,带选项,单位是4字节. 3.8位服务类型 4.16位总长度:IP报头 IP载荷 传输层是不知道载荷长度的,需要网络层来计算. IP报文 - IP报头 IP载荷 TCP报文 TCP载荷 IP载荷(TCP报文) …...
代码随想录算法训练营第四十八天| LeetCode121. 买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III
121. 买卖股票的最佳时机 题目描述: 121. 买卖股票的最佳时机. 解法 dp class Solution(object):def maxProfit(self, prices):if not prices:return 0dp0 0# 0表示不持有股票,1表示持有股票dp1 0-prices[0]for i in range(1,len(prices)):# 当前没有股票# 两…...
C++新经典10--vector以及其使用
vector vector类型是一个标准库中的类型,代表一个容器、集合或者动态数组这样一种概念。既然是容器,那就可以把若干个对象放到里面。当然,这些对象的类型必须相同。简单来说,可以把一堆int型数字放到vector容器中去,复…...
std : : vector
一.简介 std::vector 的底层实现通常基于动态数组(dynamic array),它是一种连续分配的内存块,允许元素的快速随机访问。下面是 std::vector 的一些关键特点和底层实现细节: 连续内存块:std::vector 内部使…...
AJAX学习笔记8 跨域问题及解决方案
AJAX学习笔记7 AJAX实现省市联动_biubiubiu0706的博客-CSDN博客 跨域:指一个域名的网页去请求另外一个域名资源.比如百度页面去请求京东页面资源. 同源与不同源三要素:协议,域名,端口 协议一致,域名一致,端口一致.才算是同源.其他一律不同源 新建项目测试: 1.window.open();…...
webhook--详解(gitee 推送)
一、简介 webhook 是一种基于 HTTP 的回调函数,可在 2 个应用编程接口(API)之间实现轻量级的事件驱动通信。是一种新型的前后端交互方式,一种对客户端-服务器模式的逆转,在传统方法中,客户端从服务器请求数…...
高速路自动驾驶功能HWP功能定义
一、功能定义 高速路自动驾驶功能HWP是指在一般畅通高速公路或城市快速路上驾驶员可以放开双手双脚,同时注意力可在较长时间内从驾驶环境中转移,做一些诸如看手机、接电话、看风景等活动,该系统最低工作速度为60kph。 如上两种不同环境和速度…...
Leetcode113. 路径总和 II
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 官方题解:力扣(LeetCode)官网 - 全…...
分布式锁之redis实现
docker安装redis 拉取镜像 docker pull redis:6.2.6 查看镜像 启动容器并挂载目录 需要挂在的data和redis.conf自行创建即可 docker run --restart always -d -v /usr/local/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf -v /usr/local/docker/redis/data:/dat…...
Idea中如何在一个项目中引入其他子模块?
首先在Settings打开Project Structure,然后找到Modules,点击加号点击import module,将需要引进的module引进来。 然后点击Artifacts 可以看到比如说day22…这个是我现在的项目,day16是我需要引入的。那么就在红色横线上面右键点第…...
UDP协议概述
传输层里比较重要的两个协议,一个是 TCP,一个是 UDP。TCP 是面向连接的,UDP 是面向无连接的。 所谓的建立连接,是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,用这样的数据结…...
Python-tracemalloc-跟踪内存分配
tracemalloc 模块是一个用于对 python 已申请的内存块进行debug的工具。它能提供以下信息: 定位对象分配内存的位置 按文件、按行统计python的内存块分配情况: 总大小、块的数量以及块平均大小。 对比两个内存快照的差异,以便排查内存泄漏 显示前10项 显示内存…...
02 CSS技巧
02 CSS技巧 clip-path 自定义形状,或者使用自带的属性画圆等circle HTML结构 <body><div class"container"></div> </body>CSS结构 使用*polygon*自定义形状 .container {width: 300px;height: 300px;background-color: re…...
Yarn资源调度器
文章目录 一、Yarn资源调度器1、架构2、Yarn工作机制3、HDFS、YARN、MR关系4、作业提交之HDFS&MapReduce 二、Yarn调度器和调度算法1、先进先出调度器(FIFO)2、容量调度器(Capacity Scheduler)3、公平调度器(Fair …...
android上架备案公钥和md5获取工具
最近很多公司上架遇到了一个问题,就是要提供app的备案证明,现在android上架都需要备案了,但是我们的证书都是通过工具生成的,哪里知道公钥和md5那些东西呢?无论安卓备案还是ios备案都需要提供公钥和md5。 包括ios的备案…...
SpringBoot系列(12):SpringBoot集成log4j2日志配置
最近项目上有使用到log4j2日志模板配置,本文简单总结一下之前的学习笔记,如有纰漏之处,请批评指正。 1. log4j2日志依赖 使用log4j2日志模板时,需要引入相关依赖,下边的两种依赖方式均可。 1.1 使用sl4j依赖时 <…...
HTML事件列表
鼠标事件 属性描述DOMonclick当用户点击某个对象时调用的事件句柄。2oncontextmenu在用户点击鼠标右键打开上下文菜单时触发ondblclick当用户双击某个对象时调用的事件句柄。2onmousedown鼠标按钮被按下。2onmouseenter当鼠标指针移动到元素上时触发。2onmouseleave当鼠标指针…...
并发-Executor框架笔记
Executor框架 jdk5开始,把工作单元与执行机制分离开来,工作单元包括Runable和Callable,执行机制由Executor框架来提供。 Executor框架简介 Executor框架的两级调度模型 Java线程被一对一映射为本地操作系统线程 java线程启动会创建一个本…...
【C进阶】分析 C/C++程序的内存开辟与柔性数组(内有干货)
前言: 本文是对于动态内存管理知识后续的补充,以及加深对其的理解。对于动态内存管理涉及的大部分知识在这篇文章中 ---- 【C进阶】 动态内存管理_Dream_Chaser~的博客-CSDN博客 本文涉及的知识内容主要在两方面: 简单解析C/C程序…...
深入理解 JVM 之——字节码指令与执行引擎
更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 类文件结构 Write Once,Run Anywhere 对于 C 语言从程序到运行需要经过编译的过程,只有经历了编译后,我们所编写的代码才能够翻译为机器可以直接运行的二进制代码&#x…...
C++:vector
目录 一、关于vector 二、vector的相关函数 三、相关函数的使用 ①构造函数 ②size ③[] 编辑 ④push_back ⑤迭代器iterator ⑥reserve ⑦resize ⑧find ⑨insert ⑩erase ⑪sort 一、关于vector vector比较像数组 观察可知,vector有两个模板参数…...
Android Automotive编译
系统准备 安装系统 准备一台安装Ubuntu系统的机器(windows系统的机器可以通过WSL安装ubuntu系统) 安装docker 本文使用docker进行编译,因此提前安装docker。参考网络链接安装docker并设置为不使用sudo进行docker操作。 参考链接ÿ…...
什么是50ETF期权开户条件,怎么开期权交易权限?
50ETF期权是指上证50ETF期权,标的物是上证50ETF,代码是(510500),期权是一种在上证50ETF基础上进行衍生品交易的金融工具,下文科普什么是50ETF期权开户条件,怎么开期权交易权限?本文来…...
浙江 网站建设/怎么引流客源最好的方法
文章目录1. RAID1.1 RAID磁盘陈列介绍1.2 RAID 0(条带化存储)1.3 RAID 1(镜像存储)1.4 RAID 5磁盘阵列介绍1.5 RAID 6磁盘陈列介绍1.6 RAID 10磁盘阵列介绍1.7 阵列卡介绍2.硬RAID创建磁盘阵列3.软RAID创建磁盘阵列3.1 实例:软创建RAID53.2 实…...
宁波网站建设有限公司/沈阳网站seo排名公司
仅作为记录,大佬请跳过。 文章目录展示参考try{folder_group_index Convert.ToInt32(item.LabelInfo.Dimensioning);}catch{folder_group_index_string "unregular";}展示 参考 感谢大佬博主文章:传送门...
wordpress 获取页面id/肇庆疫情最新情况
2019独角兽企业重金招聘Python工程师标准>>> 一般情况下使用 import xlwt 但是这个保存的文件格式实际上是excel 2003的格式,列对多只能255列 import xlrd 用来读取excel,源代码读取列只能是 0 < x < 255 ,需要手动改一下源代码 不能使用xlwt保存文件为xlsx文…...
网站服务器证书过期怎么解决/百度关键词优化手段
有关Server.Mappath详细接触 [ 2005-07-21 23:15:33 | 作者: xbear ] 先看图:1,首先在本机E盘下面建立red文件夹,夹子内继续建立conn.asp文件,文件内容如下:<%Server.Mappath("database/cnbruce.mdb")%&g…...
wordpress api文章列表接口/南通百度seo代理
Github项目地址:https://github.com/feser-xuan/Arithmetic.git1、需求分析软件基本功能要求如下:程序可接收一个输入参数n,然后随机产生n道加减乘除练习题,每个数字在 0 和 100 之间,运算符在3个到5个之间。为了让小学…...
哪个浏览器可以做网站/千锋教育
提供:ZStack云计算 系列教程 本教程为如何利用SNMP实现网络监控与管理系列三篇中的第三篇。 内容介绍 相当一部分系统管理员正在积极收集与服务器及基础设施相关的信息。可供选择的工具与方案多种多样,而其中相当一部分以SNMP技术为基础。 SNMP的全…...