手写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 对象时,需要递归处理。
    • 最后在属性填充时需要用到反射操作,也可以使用一些工具类处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/128675.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

21.添加websocket模块

这里默认读者了解websocket协议&#xff0c;若是还不了解可以看下这篇文章wesocket协议。 websocket主要有三个步骤&#xff0c;1通过HTTP进行握手连接&#xff0c;2进行双向通信&#xff0c;3.协商断开连接 第一步的握手连接需要HTTP&#xff0c;所以还需要使用到上一节讲解…

Python实现猎人猎物优化算法(HPO)优化BP神经网络回归模型(BP神经网络回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 猎人猎物优化搜索算法(Hunter–prey optimizer, HPO)是由Naruei& Keynia于2022年提出的一种最新的…

OpenCV(三十三):计算轮廓面积与轮廓长度

1.介绍轮廓面积与轮廓长度 轮廓面积&#xff08;Contour Area&#xff09;是指轮廓所包围的区域的总面积。通常情况下&#xff0c;轮廓面积的单位是像素的平方。 轮廓长度&#xff08;Contour Length&#xff09;又称周长&#xff08;Perimeter&#xff09;&#xff0c;表示轮廓…

Unity 从0开始编写一个技能编辑器_01_分析需求

入职以来一直很想实现一个技能编辑器&#xff0c;在积累了一些经验以后&#xff0c;决定利用ScriptableObject开发一个&#xff0c;在此记录 1.简单的需求分析 在游戏开发中&#xff0c;技能系统是一个至关重要的组成部分。技能决定了游戏角色可以执行的各种动作&#xff0c;例…

代码随想录算法训练营第十八天|513. 找树左下角的值|112. 路径总和|106. 从中序与后序遍历序列构造二叉树

513. 找树左下角的值 题目&#xff1a;给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 思路一&#xff1a;层序遍历&#xff0c;最后一层的第一个元素&#xff0c;即…

java实时监控mysql数据库变化

对于二次开发来说&#xff0c;很大一部分就找找文件和找数据库的变化情况 对于数据库变化。还没有发现比较好用的监控数据库变化监控软件。 今天&#xff0c;我就给大家介绍一个如何使用mysql自带的功能监控数据库变化 1、打开数据库配置文件my.ini &#xff08;一般在数据库…

c语言 2.0

1.数据类型 数据类型介绍 数据类型&#xff1a;c语言中数据类型有3种&#xff0c;分别是基本数据类型、构造数据类型、指针数据类型。 数据类型的作用&#xff1a;编译器预算数据分配的内存空间大小。 ps&#xff1a;可以通俗理解为&#xff1a;数据类型是用来规范内存的开销…

python DVWA文件上传POC练习

先直接测试POC 抓包 GET /dv/vulnerabilities/sqli/?id1%27unionselect1%2Cmd5%28123%29%23&SubmitSubmit HTTP/1.1Host: 10.9.75.161Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrom…

Tomcat服务的部署及配置优化

文章目录 1. Tomcat的相关介绍1.1 Tomcat简介1.2 Tomcat的核心组件1.2.1 Web容器1.2.2 Servlet容器1.2.3 JSP容器 1.3 Tomcat的功能组件1.3.1 connector连接器1.3.2 container容器1.3.2.1 子容器及其相关功能 1.4 主要作用1.5 Tmocat处理请求的过程 2. Tomcata服务部署2.1 安装…

log4qt库的使用

log4qt库的使用 一,什么是log4qt?二,log4qt的下载三,如何集成log4qt?1.在vs2022中集成log4qt的方法:模块一:配置log4qt的步骤步骤一,将下好的log4qt库进行解压,然后再库文件中,新建build和Log4Qt文件夹步骤二,打开cmake,有两个填写路径的位置.步骤三,点击cmake的configure按钮…

tcp满开始和拥塞避免

tcp的拥塞控制有四种算法&#xff0c;后面的快重传和快恢复是后面新增的&#xff0c; 刚开始会初始化慢开始门限值&#xff0c;并将拥塞窗口值为1往网络中发送&#xff0c;若收到确认包则将拥塞窗口翻倍&#xff0c;执行慢开始算法&#xff0c;当拥塞窗口值达到慢开始门限后&am…

02-Tomcat打破双亲委派机制

上一篇&#xff1a;01-从JDK源码级别剖析JVM类加载机制 Tomcat 如果使用默认的双亲委派类加载机制行不行&#xff1f; 我们思考一下&#xff1a;Tomcat是个web容器&#xff0c; 那么它要解决什么问题&#xff1a; 一个web容器可能需要部署两个应用程序&#xff0c;不同的应用…

Alibaba(获得店铺的所有商品) API 接口

为了进行电商平台 的API开发&#xff0c;首先我们需要做下面几件事情。 1&#xff09;开发者注册一个账号 2&#xff09;然后为每个alibaba应用注册一个应用程序键&#xff08;App Key) 。 3&#xff09;下载alibaba API的SDK并掌握基本的API基础知识和调用 4&#xff09;利…

深入浅出Android同步屏障机制

原文链接 Android Sync Barrier机制 诡异的假死问题 前段时间&#xff0c;项目上遇到了一个假死问题&#xff0c;随机出现&#xff0c;无固定复现规律&#xff0c;大量频繁随机操作后&#xff0c;便会出现假死&#xff0c;整个应用无法操作&#xff0c;不会响应事件&#xff…

【Linux】Systemd 中的单元(Unit)和单元文件(Unit File)怎么理解?

单元&#xff08;Unit&#xff09;单元文件&#xff08;Unit File&#xff09;感谢 &#x1f496; 关于systemd是什么&#xff0c;http://t.csdn.cn/pMkG7这篇文章里有详细说明。 这篇文件我们一起来看看Systemd 中的单元&#xff08;Unit&#xff09;和单元文件&#xff08;Un…

vue使用jsencrypt实现rsa前端加密

实现 RSA 加密 介绍 vue 完成 rsa 加密传输&#xff0c;jsencrypt 实现参数的前端加密 1 安装 jsencrypt npm install jsencrypt2 编写 jsencrypt.js 在 utils 文件夹中新建 jsencrypt.js 文件&#xff0c;内容如下&#xff1a;注意点&#xff1a;一般公钥都是后端生成好的&a…

excl在建模语言中的运用

目录 1.表格的定位 2.数学函数 3.自动填充功能 4.数据透视表的应用 5.切片器 6. Date(),time(),now()&#xff0c;today() 7.文本转日期 8.分裂 9.sumif函数 10.数字转换为文本的方法 11.SUMIFS()函数&#xff1a;多个条件筛选 12.宏 13.提取多个表中&#xff0c;…

大秒杀系统设计

参考链接&#xff1a;http://www.taodudu.cc/news/show-5770725.html?actiononClick 1. 一些数据 大家还记得2013年的小米秒杀吗&#xff1f;三款小米手机各11万台开卖&#xff0c;走的都是大秒系统&#xff0c;3分钟后成为双十一第一家也是最快破亿的旗舰店。 经过日志统计…

appium+jenkins实例构建

自动化测试平台 Jenkins简介 是一个开源软件项目&#xff0c;是基于java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 前面我们已经开完测试脚本&#xff0c;也使用bat 批处…

文件上传之图片码混淆绕过(upload的16,17关)

目录 1.upload16关 1.上传gif loadup17关&#xff08;文件内容检查&#xff0c;图片二次渲染&#xff09; 1.上传gif&#xff08;同上面步骤相同&#xff09; 2.条件竞争 1.upload16关 1.上传gif imagecreatefromxxxx函数把图片内容打散&#xff0c;&#xff0c;但是不会…