十一 手写Spring框架

十一、手写Spring框架

Spring IoC容器的实现原理:工厂模式 + 解析XML + 反射机制。

我们给自己的框架起名为:loveSpring

第一步:创建模块loveSpring

采用Maven方式新建Module:loveSpring

打包方式采用jar,并且引入dom4j和jaxen的依赖,因为要使用它解析XML文件,还有junit依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.loveSpringframework</groupId><artifactId>loveSpring</artifactId><version>1.0.0</version><packaging>jar</packaging><dependencies><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version></dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.2.0</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency></dependencies><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties></project>

第二步:准备好我们要管理的Bean

准备好我们要管理的Bean(这些Bean在将来开发完框架之后是要删除的

注意包名,不要用org.loveSpringframework包,因为这些Bean不是框架内置的。是将来使用我们框架的程序员提供的。

package com.study.loveSpring.bean;/*** @author sqnugy* @version 1.0* @className Address* @since 1.0**/
public class Address {private String city;private String street;private String zipcode;public Address() {}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}public String getZipcode() {return zipcode;}public void setZipcode(String zipcode) {this.zipcode = zipcode;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +", zipcode='" + zipcode + '\'' +'}';}
}
package com.study.loveSpring.bean;/*** @author sqnugy* @version 1.0* @className User* @since 1.0**/
public class User {private String name;private int age;private Address addr;public User() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", addr=" + addr +'}';}
}

第三步:准备loveSpring.xml配置文件

将来在框架开发完毕之后,这个文件也是要删除的。因为这个配置文件的提供者应该是使用这个框架的程序员。

文件名随意,我们这里叫做:loveSpring.xml

文件放在类路径当中即可,我们这里把文件放到类的根路径下。

<?xml version="1.0" encoding="UTF-8"?>
<beans><bean id="userBean" class="com.study.loveSpring.bean.User"><property name="name" value="张三"/><property name="age" value="20"/><property name="addr" ref="addrBean"/></bean><bean id="addrBean" class="com.study.loveSpring.bean.Address"><property name="city" value="北京"/><property name="street" value="大兴区"/><property name="zipcode" value="1000001"/></bean></beans>

使用value给简单属性赋值。使用ref给非简单属性赋值。

第四步:编写ApplicationContext接口

ApplicationContext接口中提供一个getBean()方法,通过该方法可以获取Bean对象。

注意包名:这个接口就是loveSpring框架中的一员了。

package org.loveSpringframework.core;/*** @author sqnugy* @version 1.0* @className ApplicationContext* @since 1.0**/
public interface ApplicationContext {/*** 根据bean的id获取bean实例。* @param beanId bean的id* @return bean实例*/Object getBean(String beanId);
}

第五步:编写ClassPathXmlApplicationContext

ClassPathXmlApplicationContextApplicationContext接口的实现类。该类从类路径当中加载loveSpring.xml配置文件。

package org.loveSpringframework.core;/*** @author sqnugy* @version 1.0* @className ClassPathXmlApplicationContext* @since 1.0**/
public class ClassPathXmlApplicationContext implements ApplicationContext{@Overridepublic Object getBean(String beanId) {return null;}
}

第六步:确定采用Map集合存储Bean

确定采用Map集合存储Bean实例。Map集合的key存储beanId,value存储Bean实例。

Map<String,Object>ClassPathXmlApplicationContext类中添加Map<String,Object>属性。

并且在ClassPathXmlApplicationContext类中添加构造方法,该构造方法的参数接收loveSpring.xml文件。

同时实现getBean方法。

package org.loveSpringframework.core;import java.util.HashMap;
import java.util.Map;/*** @author sqnugy* @version 1.0* @className ClassPathXmlApplicationContext* @since 1.0**/
public class ClassPathXmlApplicationContext implements ApplicationContext{/*** 存储bean的Map集合*/private Map<String,Object> beanMap = new HashMap<>();/*** 在该构造方法中,解析loveSpring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。* @param resource 配置文件路径(要求在类路径当中)*/public ClassPathXmlApplicationContext(String resource) {}@Overridepublic Object getBean(String beanId) {return beanMap.get(beanId);}
}

第七步:解析配置文件实例化所有Bean

ClassPathXmlApplicationContext的构造方法中解析配置文件,获取所有bean的类名,通过反射机制调用无参数构造方法创建Bean。并且将Bean对象存放到Map集合中。

/**
* 在该构造方法中,解析loveSpring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。
* @param resource 配置文件路径(要求在类路径当中)
*/
public ClassPathXmlApplicationContext(String resource) {try {SAXReader reader = new SAXReader();Document document = reader.read(ClassLoader.getSystemClassLoader().getResourceAsStream(resource));// 获取所有的bean标签List<Node> beanNodes = document.selectNodes("//bean");// 遍历集合beanNodes.forEach(beanNode -> {Element beanElt = (Element) beanNode;// 获取idString id = beanElt.attributeValue("id");// 获取classNameString className = beanElt.attributeValue("class");try {// 通过反射机制创建对象Class<?> clazz = Class.forName(className);Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();Object bean = defaultConstructor.newInstance();// 存储到Map集合beanMap.put(id, bean);} catch (Exception e) {e.printStackTrace();}});} catch (Exception e) {e.printStackTrace();}
}

第八步:测试能否获取到Bean

编写测试程序。

package com.study.loveSpring.test;import org.junit.Test;
import org.loveSpringframework.core.ApplicationContext;
import org.loveSpringframework.core.ClassPathXmlApplicationContext;/*** @author sqnugy* @version 1.0* @className loveSpringTest* @since 1.0**/
public class loveSpringTest {@Testpublic void testloveSpring(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("loveSpring.xml");Object userBean = applicationContext.getBean("userBean");Object addrBean = applicationContext.getBean("addrBean");System.out.println(userBean);System.out.println(addrBean);}
}

执行结果:

通过测试Bean已经实例化成功了,属性的值是null,这是我们能够想到的,毕竟我们调用的是无参数构造方法,所以属性都是默认值。

下一步就是我们应该如何给Bean的属性赋值呢?

第九步:给Bean的属性赋值

通过反射机制调用set方法,给Bean的属性赋值。

继续在ClassPathXmlApplicationContext构造方法中编写代码。

package org.loveSpringframework.core;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author sqnugy* @version 1.0* @className ClassPathXmlApplicationContext* @since 1.0**/
public class ClassPathXmlApplicationContext implements ApplicationContext{/*** 存储bean的Map集合*/private Map<String,Object> beanMap = new HashMap<>();/*** 在该构造方法中,解析loveSpring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。* @param resource 配置文件路径(要求在类路径当中)*/public ClassPathXmlApplicationContext(String resource) {try {SAXReader reader = new SAXReader();Document document = reader.read(ClassLoader.getSystemClassLoader().getResourceAsStream(resource));// 获取所有的bean标签List<Node> beanNodes = document.selectNodes("//bean");// 遍历集合(这里的遍历只实例化Bean,不给属性赋值。为什么要这样做?)beanNodes.forEach(beanNode -> {Element beanElt = (Element) beanNode;// 获取idString id = beanElt.attributeValue("id");// 获取classNameString className = beanElt.attributeValue("class");try {// 通过反射机制创建对象Class<?> clazz = Class.forName(className);Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();Object bean = defaultConstructor.newInstance();// 存储到Map集合beanMap.put(id, bean);} catch (Exception e) {e.printStackTrace();}});// 再重新遍历集合,这次遍历是为了给Bean的所有属性赋值。// 思考:为什么不在上面的循环中给Bean的属性赋值,而在这里再重新遍历一次呢?// 通过这里你是否能够想到Spring是如何解决循环依赖的:实例化和属性赋值分开。beanNodes.forEach(beanNode -> {Element beanElt = (Element) beanNode;// 获取bean的idString beanId = beanElt.attributeValue("id");// 获取所有property标签List<Element> propertyElts = beanElt.elements("property");// 遍历所有属性propertyElts.forEach(propertyElt -> {try {// 获取属性名String propertyName = propertyElt.attributeValue("name");// 获取属性类型Class<?> propertyType = beanMap.get(beanId).getClass().getDeclaredField(propertyName).getType();// 获取set方法名String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);// 获取set方法Method setMethod = beanMap.get(beanId).getClass().getDeclaredMethod(setMethodName, propertyType);// 获取属性的值,值可能是value,也可能是ref。// 获取valueString propertyValue = propertyElt.attributeValue("value");// 获取refString propertyRef = propertyElt.attributeValue("ref");Object propertyVal = null;if (propertyValue != null) {// 该属性是简单属性String propertyTypeSimpleName = propertyType.getSimpleName();switch (propertyTypeSimpleName) {case "byte": case "Byte":propertyVal = Byte.valueOf(propertyValue);break;case "short": case "Short":propertyVal = Short.valueOf(propertyValue);break;case "int": case "Integer":propertyVal = Integer.valueOf(propertyValue);break;case "long": case "Long":propertyVal = Long.valueOf(propertyValue);break;case "float": case "Float":propertyVal = Float.valueOf(propertyValue);break;case "double": case "Double":propertyVal = Double.valueOf(propertyValue);break;case "boolean": case "Boolean":propertyVal = Boolean.valueOf(propertyValue);break;case "char": case "Character":propertyVal = propertyValue.charAt(0);break;case "String":propertyVal = propertyValue;break;}setMethod.invoke(beanMap.get(beanId), propertyVal);}if (propertyRef != null) {// 该属性不是简单属性setMethod.invoke(beanMap.get(beanId), beanMap.get(propertyRef));}} catch (Exception e) {e.printStackTrace();}});});} catch (Exception e) {e.printStackTrace();}}@Overridepublic Object getBean(String beanId) {return beanMap.get(beanId);}
}

重点处理:当property标签中是value怎么办?是ref怎么办?

执行测试程序:

第十步:打包发布

将多余的类以及配置文件删除,使用maven打包发布。

第十一步:站在程序员角度使用loveSpring框架

新建模块:loveSpring-test

引入loveSpring框架的依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.study</groupId><artifactId>loveSpring-test</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><dependencies><dependency><groupId>org.loveSpringframework</groupId><artifactId>loveSpring</artifactId><version>1.0.0</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency></dependencies><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties></project>

编写Bean

package com.study.loveSpring.bean;/*** @author sqnugy* @version 1.0* @className UserDao* @since 1.0**/
public class UserDao {public void insert(){System.out.println("UserDao正在插入数据");}
}
package com.study.loveSpring.bean;/*** @author sqnugy* @version 1.0* @className UserService* @since 1.0**/
public class UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void save(){System.out.println("UserService开始执行save操作");userDao.insert();System.out.println("UserService执行save操作结束");}
}

编写loveSpring.xml文件

<?xml version="1.0" encoding="UTF-8"?><beans><bean id="userServiceBean" class="com.study.loveSpring.bean.UserService"><property name="userDao" ref="userDaoBean"/></bean><bean id="userDaoBean" class="com.study.loveSpring.bean.UserDao"/></beans>

编写测试程序

package com.study.loveSpring.test;import com.study.loveSpring.bean.UserService;
import org.junit.Test;
import org.loveSpringframework.core.ApplicationContext;
import org.loveSpringframework.core.ClassPathXmlApplicationContext;/*** @author sqnugy* @version 1.0* @className loveSpringTest* @since 1.0**/
public class loveSpringTest {@Testpublic void testloveSpring(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("loveSpring.xml");UserService userServiceBean = (UserService) applicationContext.getBean("userServiceBean");userServiceBean.save();}
}

执行结果

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

360多模态及文档理解大模型技术亮相全球机器学习技术大会,共探AI技术新前沿...

北京&#xff0c;2024年11月15日 —— 在人工智能技术飞速发展的今天&#xff0c;全球技术生态正经历着深刻的变革。2024全球机器学习技术大会&#xff08;北京站&#xff09;于11月14-15日在北京举行&#xff0c;汇聚了顶尖的AI专家、学者和行业实践者&#xff0c;共同探讨机器…

六自由度双足机器人运动控制

最近迷上了研究机器人&#xff0c;花了很多时间研究机器人的控制和交互。先后开发出来了四足四自自由度&#xff0c;四足八自由度&#xff0c;两足四自由度&#xff0c;两足六自由度机器人&#xff0c;并为他们开发了相应的大模型语音交互。通过努力&#xff0c;既锻炼了动手组…

shell脚本(2)

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;shell编程&#xff08;2&#xff09;永久环境变量和字符串显位_哔哩哔哩_bilibili 本文主要讲解临时变量和永久变量以及字符串长度截取操作。 一…

SEW MDX61B 变频器调试说明

SEW MDX61B 变频器调试说明 1、打开MOVITOOLS MotionStudio软件 2、创建新项目(可根据需求更改项目名称及保存路径) 新建完成 3、电机初始化 连接新变频器和新电机时,必须进行电机初始化。电机初始化目的为配对电机参数至变频器,简单说就是让变频器知道需要控制的是什么…

【软件测试】设计测试用例的万能公式

文章目录 概念设计测试用例的万能公式常规思考逆向思维发散性思维万能公式水杯测试弱网测试如何进行弱网测试 安装卸载测试 概念 什么是测试用例&#xff1f; 测试⽤例&#xff08;Test Case&#xff09;是为了实施测试⽽向被测试的系统提供的⼀组集合&#xff0c;这组集合包…

泛微OA 请求外部数据源

1 .oa 外部数据源配置好 取数据源名称 引用key 固定写法 datasource.A_nc datasource.数据源名称 getConnection("datasource.A_nc",xf);//A账 2 引用方式 package weaver.interfaces.jphr;import java.io.UnsupportedEncodingException; import java.sql.Conne…

深度学习基础—Bleu得分

引言 机器翻译任务中&#xff0c;通常会需要评价指标来评估机器翻译的好坏。仅通过统计翻译词在标准翻译中出现的次数这种方式很不合理&#xff0c;就需要用到Bleu得分来进行评估。 1.n-gram&#xff08;N元组&#xff09; 假设要翻译&#xff1a;Le chat est sur le tapis&am…

794: 最近对问题

解法&#xff1a; #include<bits/stdc.h> using namespace std; const int N1e33; struct P{int x,y; }a[N]; int main(int argc, char** argv) {int t,n;cin>>t;while (t--){cin>>n;for (int i0;i<n;i) cin>>a[i].x>>a[i].y;double dis,mn1…

Vue基础(1)_模板语法、数据绑定

模板语法 Vue模板语法有2大类&#xff1a; 1、插值语法&#xff1b; 功能&#xff1a;用于解析标签体内内容。 写法&#xff1a;{{xxx}}&#xff0c;xxx是js表达式&#xff0c;且可以直接读取到data中的所有属性。 2、指令语法&#xff1a; 功能&#xff1a;用于解析标签(包括…

如何清洗电水壶中的水垢亲自实践

以前看过很多生活小妙招&#xff0c;什么柠檬啊&#xff0c;白醋啊&#xff0c;土豆片啊&#xff0c;都测试过。没有用。因为自来水很硬&#xff0c;钙比较重。 钙覆盖在水壶底部&#xff0c;烧水就滋滋得响&#xff0c;而且效率变低。 昨天买洁厕剂&#xff0c;看到一种除垢…

LC13:滑动窗口

文章目录 1052. 爱生气的书店老板 这个专栏记录自己刷题碰到的有关滑动窗口的题目。 1052. 爱生气的书店老板 题目链接&#xff1a;1052. 爱生气的书店老板 第一感应该是滑动窗口可以解决的&#xff0c;随后思考并写了几个版本&#xff0c;最终版本实现结合滑动窗口一次遍历…

酒店管理系统(源码+文档+部署+讲解)

本文将深入解析“酒店管理系统”的项目&#xff0c;探究其架构、功能以及技术栈&#xff0c;并分享获取完整源码的途径。 系统概述 酒店管理系统是一款为酒店行业设计的全面管理软件&#xff0c;旨在通过集成酒店运营的各个关键环节&#xff0c;提高酒店的管理效率和客户满意…

D3开发的基本框架步骤

D3.js 是一个功能强大的数据可视化库&#xff0c;用于在网页上创建复杂的图表和交互式图形。以下是一个基本的 D3.js 开发框架&#xff0c;包括了常见的步骤和代码示例&#xff0c;帮助你快速入门。 基本框架 引入 D3.js 库设置 SVG 容器加载数据创建比例尺绘制图形添加轴添加…

正则表达式完全指南,总结全面通俗易懂

目录 元字符 连接符 限定符 定位符 修饰符&#xff08;标记&#xff09; 运算符优先级 普通字符集及其替换 零宽断言 正向先行断言 负向先行断言 正向后发断言 负向后发断言 正则表达式在线测试: 正则在线测试工具 元字符 字符描述\d 匹配一个数字字符。等价于 …

对象的初步认识

#对象可组织数据&#xff08;如统计数据的表格&#xff09; 下以表格为例 1.设计一个表格:(None为初始值设定&#xff0c;表示无) class a; ##1None ##2None 2.创建一个表格 变量a 3.对对象的属性进行赋值 变量.##1"##" 变量.##2"##" 4.查询对象中…

Linux驱动开发第1步_了解STM32MP157D

了解STM32MP157D有哪些硬件资源&#xff0c;为以后进行Linux驱动开发做准备。 STM32MP157A/D器件基于高性能双核ArmCortex-A7 32位RISC内核&#xff0c;工作频率高达800 MHz。Cortex-A7处理器&#xff1a;每个CPU具有32kbyte L1指令缓存&#xff0c;每个CPU具有32kbyte L1数据…

w039基于Web足球青训俱乐部管理后台系统开发

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0…

windows C#-编写 C# LINQ 查询(上)

介绍性的语言集成查询 (LINQ) 文档中的大多数查询是使用 LINQ 声明性查询语法编写的。 但是在编译代码时&#xff0c;查询语法必须转换为针对 .NET 公共语言运行时 (CLR) 的方法调用。 这些方法调用会调用标准查询运算符(名称为 Where、Select、GroupBy、Join、Max 和 Average …

c++入门->类的定义以及this详细解析

1.对象的定义 可以在内部定义变量还有函数&#xff0c;而其中的public是声明这里面的东西是共有的&#xff0c;可以调用也可以修改&#xff0c;但是private则是声明这里面的东西是私有的不可以被改变的&#xff0c;但是在类里面共有的是可以使用私有的。 在对象中默认都是私有…

SAP- 不写代码创建报表

创建报表 点击报保存&#xff0c;表Z0708创建完成 关联三张数据库底表 点击返回 点击报保存&#xff0c;表Z0708创建完成 创建报表