当前位置: 首页 > news >正文

手写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 创建后,开始补全属性信息,那么就可以在类 AbstractAutowireCapableBeanFactorycreateBean 方法中添加补全属性方法。

在这里插入图片描述

  • 属性填充要在类实例化创建之后,也就是需要在 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;}
}
  • 这个类主要包括三个方法:createBeancreateBeanInstanceapplyPropertyValues,这里我们主要关注 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章-注入属性和依赖对象

文章目录 一、目标&#xff1a;注入属性和依赖对象二、设计&#xff1a;注入属性和依赖对象三、实现&#xff1a;注入属性和依赖对象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 集合框架&#xff08;容器&#xff09;背后对应的数据结构 3.3 相关的Java知识 3.4 什么是算法 3.5 如何学好数据结构和算法 一、集合框架的概念 Java 集合框架&#xff0c;…...

目标检测笔记(十五): 使用YOLOX完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)

文章目录 一、目标检测介绍二、YOLOX介绍三、源码获取四、环境搭建4.1 环境检测 五、数据集准备六、模型训练七、模型验证八、模型测试 一、目标检测介绍 目标检测&#xff08;Object Detection&#xff09;是计算机视觉领域的一项重要技术&#xff0c;旨在识别图像或视频中的…...

深眸科技自研轻辙视觉引擎,以AI机器视觉赋能杆号牌识别与分拣

电线杆号牌作为电力行业标识的一种&#xff0c;相当于电线杆的“身份证”&#xff0c;担负着宣传电力知识、安全警示的作用&#xff0c;用于户外使用标记输电线路电压等级、线路名称、杆塔编号等&#xff0c;能够清晰地记录电力线路杆的信息&#xff0c;并为电力线路的更改以及…...

Shell命令管理进程

Shell命令管理进程 列出进程 ps命令 top命令 管理后台进程 启动后台进程 查看后台进程 jobs和ps的区别 停止进程 Linux除了是一种多用户操作系统之外&#xff0c;还是一种多任务系统。多任务意味着可以同时运行多个程序。Linux 提供了相关的工具来列出运行中的进程,监视…...

python创建exe文件

1、搭建环境 pip install pyinstaller 2、准备测试代码 exe_test.py import timeprint("hello") print("hello") print("hello") print("hello")time.sleep(5) 注&#xff1a;添加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表示不持有股票&#xff0c;1表示持有股票dp1 0-prices[0]for i in range(1,len(prices)):# 当前没有股票# 两…...

C++新经典10--vector以及其使用

vector vector类型是一个标准库中的类型&#xff0c;代表一个容器、集合或者动态数组这样一种概念。既然是容器&#xff0c;那就可以把若干个对象放到里面。当然&#xff0c;这些对象的类型必须相同。简单来说&#xff0c;可以把一堆int型数字放到vector容器中去&#xff0c;复…...

std : : vector

一.简介 std::vector 的底层实现通常基于动态数组&#xff08;dynamic array&#xff09;&#xff0c;它是一种连续分配的内存块&#xff0c;允许元素的快速随机访问。下面是 std::vector 的一些关键特点和底层实现细节&#xff1a; 连续内存块&#xff1a;std::vector 内部使…...

AJAX学习笔记8 跨域问题及解决方案

AJAX学习笔记7 AJAX实现省市联动_biubiubiu0706的博客-CSDN博客 跨域:指一个域名的网页去请求另外一个域名资源.比如百度页面去请求京东页面资源. 同源与不同源三要素:协议,域名,端口 协议一致,域名一致,端口一致.才算是同源.其他一律不同源 新建项目测试: 1.window.open();…...

webhook--详解(gitee 推送)

一、简介 webhook 是一种基于 HTTP 的回调函数&#xff0c;可在 2 个应用编程接口&#xff08;API&#xff09;之间实现轻量级的事件驱动通信。是一种新型的前后端交互方式&#xff0c;一种对客户端-服务器模式的逆转&#xff0c;在传统方法中&#xff0c;客户端从服务器请求数…...

高速路自动驾驶功能HWP功能定义

一、功能定义 高速路自动驾驶功能HWP是指在一般畅通高速公路或城市快速路上驾驶员可以放开双手双脚&#xff0c;同时注意力可在较长时间内从驾驶环境中转移&#xff0c;做一些诸如看手机、接电话、看风景等活动&#xff0c;该系统最低工作速度为60kph。 如上两种不同环境和速度…...

Leetcode113. 路径总和 II

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 官方题解&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全…...

分布式锁之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&#xff0c;然后找到Modules&#xff0c;点击加号点击import module&#xff0c;将需要引进的module引进来。 然后点击Artifacts 可以看到比如说day22…这个是我现在的项目&#xff0c;day16是我需要引入的。那么就在红色横线上面右键点第…...

UDP协议概述

传输层里比较重要的两个协议&#xff0c;一个是 TCP&#xff0c;一个是 UDP。TCP 是面向连接的&#xff0c;UDP 是面向无连接的。 所谓的建立连接&#xff0c;是为了在客户端和服务端维护连接&#xff0c;而建立一定的数据结构来维护双方交互的状态&#xff0c;用这样的数据结…...

Python-tracemalloc-跟踪内存分配

tracemalloc 模块是一个用于对 python 已申请的内存块进行debug的工具。它能提供以下信息: 定位对象分配内存的位置 按文件、按行统计python的内存块分配情况: 总大小、块的数量以及块平均大小。 对比两个内存快照的差异&#xff0c;以便排查内存泄漏 显示前10项 显示内存…...

02 CSS技巧

02 CSS技巧 clip-path 自定义形状&#xff0c;或者使用自带的属性画圆等circle HTML结构 <body><div class"container"></div> </body>CSS结构 使用*polygon*自定义形状 .container {width: 300px;height: 300px;background-color: re…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

DBLP数据库是什么?

DBLP&#xff08;Digital Bibliography & Library Project&#xff09;Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高&#xff0c;数据库文献更新速度很快&#xff0c;很好地反映了国际计算机科学学术研…...

基于鸿蒙(HarmonyOS5)的打车小程序

1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

Java后端检查空条件查询

通过抛出运行异常&#xff1a;throw new RuntimeException("请输入查询条件&#xff01;");BranchWarehouseServiceImpl.java // 查询试剂交易&#xff08;入库/出库&#xff09;记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...