spring之手写框架

news2024/10/5 14:22:02

文章目录

  • 前言
  • 一、手写spring框架之核心接口实现
  • 二、手写spring框架之实例化Bean
  • 三、手写spring框架之获取所有set方法
  • 四、手写spring框架之给属性赋值
    • 4.1 非简单类型属性赋值
    • 4.2 简单类型属性赋值
  • 附:


前言

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


一、手写spring框架之核心接口实现

参考之前已知写的测试类中的第一行代码:

ApplicationContext application = new ClassPathXmlApplicationContext("myspring.xml");
//以及getBean()方法

因此编写接口类ApplicationContext:

public interface ApplicationContext {
    /**
     * 根据bean的名称获取对应的bean对象
     * @param beanName
     * @return
     */
    Object getBean(String beanName);
}

该接口类ApplicationContext对应的实现类ClassPathXmlApplicationContext:
主要功能是解析spring.xml文件,需要用到dom4j以及jaxen【需要导入对应的依赖文件】
关键代码如下:

            //获取类路径当中的资源
            SAXReader reader = new SAXReader();//dom4j解析XML文件的核心对象
            //获取一个输入流,指向配置文件  //只适合从类路径当中加载资源
            InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
            //读文件
            Document document = reader.read(in);
            //获取文档里的所有bean标签
            List<Node> nodes = document.selectNodes("//bean");

二、手写spring框架之实例化Bean

将所有的Bean对象先创建出来,也就是在Bean的三级缓存中提到的,先将Bean对象“曝光”。
在第一步解析xml文件的基础上,可以通过遍历bean标签拿到每个bean对应的id属性值、class属性值,再通过反射机制进行对象创建
关键代码:

//获取id属性它的值
String id = beanElt.attributeValue("id");
//获取class属性的值
String className = beanElt.attributeValue("class");
//通过反射机制创建对象,将其放到Map集合中,提前“曝光”
Class<?> aClass = Class.forName(className);
//获取无参数构造方法
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();//无参数构造方法
//调用无参数构造方法实例化Bean
Object bean = declaredConstructor.newInstance();//创建对象
//将Bean“曝光”,加入Map集合
singletonObjects.put(id,bean);

测试类:

public class MySpringTest {
    @Test
    public void testMySpring(){
        ApplicationContext application = new ClassPathXmlApplicationContext("myspring.xml");
    }
}

此时运行结果:
在这里插入图片描述
三个对象均创建成功,此时属性值为null

三、手写spring框架之获取所有set方法

创建对象之后,接下来的操作就是给对象的属性赋值。
需要在重新遍历一次Bean标签,获取id获取class以及需要获取该bean标签下的所有属性property标签(循环套循环)

重新遍历一遍bean标签:获取id获取class
遍历所有的属性标签时:

获取属性名
获取属性类型
拼接set方法名
获取set方法

关键代码如下:

//再次把所有的Bean标签都遍历一遍 这次主要是给对象的属性赋值
nodes.forEach(node->{
   try{
       Element beanElt = (Element) node;
       //获取id
       String id = beanElt.attributeValue("id");
       //获取className
       String className = beanElt.attributeValue("class");
       Class<?> aClass = Class.forName(className);
       //获取该bean标签下所有的属性propertry标签
       List<Element> propertys = beanElt.elements("property");
       //遍历所有的属性标签:
       propertys.forEach(property->{
          try{
               //获取属性名
               String propertyName = property.attributeValue("name");
               //获取属性类型
               Field field = aClass.getField(propertyName);
               System.out.println("属性名:"+propertyName);
               //拼接method方法
               String setMethodName = "set"+propertyName.toUpperCase().charAt(0)+propertyName.substring(1);
               //获取set方法
               Method setMethod = aClass.getDeclaredMethod(setMethodName,field.getType() );
          }catch(Exception e){
              e.printStackTrace();
          }
       });
   }catch(Exception e){
       e.printStackTrace();
  }
 });

运行结果:
在这里插入图片描述

四、手写spring框架之给属性赋值

//获取具体的值
String value = property.attributeValue("value");
String ref = property.attributeValue("ref");
if (value!=null) {
	//这个值是简单类型
}
 if (ref!=null) {
	//这个值是非简单类型
}

4.1 非简单类型属性赋值

在三获取到set方法的基础上,调用set方法==(调用set方法没有返回值)==
非简单类型与简单类型相比较为简单,

对象就是map集合中的value值,非简单类型同样也是map集合中的值,id就是ref的值

if (ref!=null) {
	//这个值是非简单类型
	//调用set方法
	setMethod.invoke(singletonObjects.get(id),singletonObjects.get(ref));
}

4.2 简单类型属性赋值

简单类型较为复杂原因在于 获取到的value值始终都是String类型的,而简单类型不只有String,还有int等等其他类型,因此需要根据属性的类型再去赋值。
在手写的框架里只支持:byte short int long float double boolean char以及八种包装类型:Byte Short Integer Long Float Double Boolean Character还有String类型

Object actualValue = null;
if (value!=null) {
//这个值是简单类型
	String propertyTypeSimpleName = field.getType().getSimpleName();
	switch (propertyTypeSimpleName){
 		case "byte":
 			actualValue = Byte.parseByte(value);
 			break;
 		case "short":
			actualValue = Short.parseShort(value);
			break;
		case "double":
			actualValue = Double.parseDouble(value);
 			break;
		case "boolean":
			actualValue = Boolean.parseBoolean(value);
 			break;
		case "int":
			actualValue = Integer.parseInt(value);
 			break;
		case "long":
			actualValue = Long.parseLong(value);
			break;
		case "float":
			actualValue = Float.parseFloat(value);
			break;
		case "char":
			actualValue = value.charAt(0);
			break;
		case "Byte":
			actualValue = Byte.valueOf(value);
			break;
		case "Short":
			actualValue = Short.valueOf(value);
			break;
		case "Integer":
			actualValue = Integer.valueOf(value);
			break;
		case "Long":
			actualValue = Long.valueOf(value);
			break;
		case "Float":
			actualValue = Float.valueOf(value);
			break;
		case "Double":
			actualValue = Double.valueOf(value);
			break;
		case "Boolean":
			actualValue = Boolean.valueOf(value);
			break;
		case "Character":
			actualValue = Character.valueOf(value.charAt(0));
			break;
		case "String":
			actualValue =value;
			break;
		}
	//调用set方法
	setMethod.invoke(singletonObjects.get(id),actualValue);
}

测试类:

public class MySpringTest {
    @Test
    public void testMySpring(){
        ApplicationContext application = new ClassPathXmlApplicationContext("myspring.xml");
        Object user = application.getBean("user");
        System.out.println(user);
        UserService userService = (UserService) application.getBean("userService");
        userService.save();
    }
}

运行结果:
赋值成功。 可以将此框架打包发布【两个类ClassPathXmlApplicationContext以及ApplicationContext】
在这里插入图片描述


附:

ClassPathXmlApplicationContext类的完整代码

package org.myspringframework;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ClassPathXmlApplicationContext implements  ApplicationContext{

    private Map<String,Object> singletonObjects = new HashMap<>();

    /**
     * 解析mybatis的配置文件,然后初始化所有的Bean对象
     * @param configLocation spring配置文件的路径
     */
    public ClassPathXmlApplicationContext(String configLocation){
        try{
            //解析mybatis.xml文件,然后实例化bean,将Bean存放到singletonObjects集合当中
            //获取类路径当中的资源
            SAXReader reader = new SAXReader();//dom4j解析XML文件的核心对象
            //获取一个输入流,指向配置文件  //只适合从类路径当中加载资源
            InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
            //读文件
            Document document = reader.read(in);
            //获取文档里的所有bean标签
            List<Node> nodes = document.selectNodes("//bean");
            //遍历bean标签
            nodes.forEach(node->{
                try{
                    //System.out.println(node);
                    //向下转型的目的是为了使用Element接口里更加丰富的方法
                    Element beanElt = (Element) node;
                    //获取id属性它的值
                    String id = beanElt.attributeValue("id");
                    //获取class属性的值
                    String className = beanElt.attributeValue("class");

                    //通过反射机制创建对象,将其放到Map集合中,提前“曝光”
                    Class<?> aClass = Class.forName(className);
                    //获取无参数构造方法
                    Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();//无参数构造方法
                    //调用无参数构造方法实例化Bean
                    Object bean = declaredConstructor.newInstance();//创建对象
                    //将Bean“曝光”,加入Map集合
                    singletonObjects.put(id,bean);
                    System.out.println(singletonObjects.toString());
                }catch(Exception e) {
                    e.printStackTrace();
                }
            });
        //再次把所有的Bean标签都遍历一遍 这次主要是给对象的属性赋值
            nodes.forEach(node->{
                try{
                    Element beanElt = (Element) node;
                    //获取id
                    String id = beanElt.attributeValue("id");
                    //获取className
                    String className = beanElt.attributeValue("class");
                    Class<?> aClass = Class.forName(className);
                    //获取该bean标签下所有的属性propertry标签
                    List<Element> propertys = beanElt.elements("property");
                    //遍历所有的属性标签:
                    propertys.forEach(property->{
                        try{
                            //获取属性名
                            String propertyName = property.attributeValue("name");
                            //获取属性类型
                            Field field = aClass.getDeclaredField(propertyName);
                            System.out.println("属性名:"+propertyName);
                            //拼接method方法
                            String setMethodName = "set"+propertyName.toUpperCase().charAt(0)+propertyName.substring(1);
                            //获取set方法
                            Method setMethod = aClass.getDeclaredMethod(setMethodName,field.getType() );
                            //获取具体的值
                            String value = property.attributeValue("value");
                            String ref = property.attributeValue("ref");
                            Object actualValue = null;
                            if (value!=null) {
                                //这个值是简单类型
                                String propertyTypeSimpleName = field.getType().getSimpleName();
                                switch (propertyTypeSimpleName){
                                    case "byte":
                                        actualValue = Byte.parseByte(value);
                                        break;
                                    case "short":
                                        actualValue = Short.parseShort(value);
                                        break;
                                    case "double":
                                        actualValue = Double.parseDouble(value);
                                        break;
                                    case "boolean":
                                        actualValue = Boolean.parseBoolean(value);
                                        break;
                                    case "int":
                                        actualValue = Integer.parseInt(value);
                                        break;
                                    case "long":
                                        actualValue = Long.parseLong(value);
                                        break;
                                    case "float":
                                        actualValue = Float.parseFloat(value);
                                        break;
                                    case "char":
                                        actualValue = value.charAt(0);
                                        break;
                                    case "Byte":
                                        actualValue = Byte.valueOf(value);
                                        break;
                                    case "Short":
                                        actualValue = Short.valueOf(value);
                                        break;
                                    case "Integer":
                                        actualValue = Integer.valueOf(value);
                                        break;
                                    case "Long":
                                        actualValue = Long.valueOf(value);
                                        break;
                                    case "Float":
                                        actualValue = Float.valueOf(value);
                                        break;
                                    case "Double":
                                        actualValue = Double.valueOf(value);
                                        break;
                                    case "Boolean":
                                        actualValue = Boolean.valueOf(value);
                                        break;
                                    case "Character":
                                        actualValue = Character.valueOf(value.charAt(0));
                                        break;
                                    case "String":
                                        actualValue =value;
                                        break;
                                }
                                //调用set方法
                                setMethod.invoke(singletonObjects.get(id),actualValue);
                            }
                            if (ref!=null) {
                                //这个值是非简单类型
                                //调用set方法
                                setMethod.invoke(singletonObjects.get(id),singletonObjects.get(ref));
                            }

                        }catch(Exception e){
                            e.printStackTrace();
                        }

                    });


                }catch(Exception e){
                    e.printStackTrace();
                }
            });

        }catch(Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String beanName) {
        return singletonObjects.get(beanName);
    }
}

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

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

相关文章

学习性能所必须的知识之算法

什么是算法? 通过有效地缩小查找范围,只需要很少的次数就能很快速的找到需要的数字,这样的策略或方法就称为“算法”。 算法的好坏对性能有很大的影响。 学习算法的窍门 掌握算法优点与缺陷,“折中”是一个很重要的思维通过在图上推演来思考评价算法的指标 通过复杂度(…

各种型号西门子PLC所支持的通信协议小结

西门子PLC有4大类&#xff0c;几十个型号类型&#xff0c;PLC不同所支持的通讯协议也不相同。 按照大类型来划分&#xff0c;具体可分为串口协议和以太网通信协议两大类。 串口协议主要有&#xff1a;MODBUS RTU 通信协议&#xff1b;PROFIBUS 通信协议&#xff1b;USS通信协…

疫情信息管理系统(附源代码及数据库)

本系统是一个可以对各种疫情进行管理的系统&#xff0c;管理员可以直接对居民、住户进行统一的管理&#xff0c;这样就能在疫情期间大大减轻了管理者的工作量&#xff0c;使管理社区的渠道更加的方便。其主要功能有&#xff1a;登录功能&#xff0c;公告的发布&#xff0c;到访…

2022, 6年技术路, 后疫情时代复盘

专注 聚焦 持续复盘写下你一年的希望...又到了每年一度的复盘时间。转眼一想, 做技术已经 6 年了。说实话&#xff0c;有点疲惫了。今年整个互联网行业都不好过, 加上疫情的反复不断, 从耳边流出了很多裁员的信息, 股市也比较低迷, 身处底层的我们只能夹缝生存。但是, 我又是…

【MySQL基础教程】DQL语句详细介绍

前言 本文为 【MySQL基础教程】DQL语句 相关内容介绍&#xff0c;下边具体将对DQL语句基本语法&#xff0c;基础查询&#xff0c;条件查询&#xff0c;聚合函数&#xff0c;分组查询&#xff0c;排序查询&#xff0c;分页查询&#xff0c;相关案例&#xff0c;执行顺序等进行详…

Elasticsearch 核心技术(二):elasticsearch-head 插件安装和使用

❤️ 个人主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;大数据核心技术从入门到精通 文章目录一、安装方式二、下载 head 插件三、安装 head 插件四、运行 head 插件五、使用…

服务器硬件规格常用查看命令——网卡相关命令

lspci 使用lspci命令可以显示系统中的PCI总线和连接到它们的设备信息&#xff0c;在默认情况下&#xff0c;显示一个简短格式的设备列表。但是可以使用“lspci -vvx”或“lspci -vvxxx”显示更加详细的设备信息&#xff0c;在这些信息中包含了PCI设备驱动程序或lspci本身的错误…

GitHub与微信开启“秘密扫描”计划,来确保数据安全

近日GitHub 官方博客更是宣布&#xff1a;" 腾讯微信现在是 GitHub 秘密扫描合作伙伴。" “秘密扫描”是Github发起的一个计划&#xff0c;可别被它名字吓到了&#xff0c;它并不是说秘密的扫描用户的隐私数据&#xff0c;而是和微信合作发起&#xff0c;防止微信开…

Redhat7上安装Red Hat Developer Toolset并自由切换gcc和g++的版本

Redhat7上安装Red Hat Developer Toolset并自由切换gcc和g的版本一、Red Hat Developer Toolset 概述二、使用Red Hat Software Collections2.1 获取pool ID2.2 将pool ID 附加到本地系统中2.3 获取仓库列表2.4 使能上述仓库三、安装 Red Hat Developer Toolset四、升级Red Hat…

大数据分析案例-基于决策树算法构建员工离职预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

在windows操作系统上安装mysql数据库

背景 写这个数据库的安装教程&#xff0c;是为了后续文章中在windows系统中搭建测试环境要用到数据库做铺垫&#xff0c;不是所有的人都有云服务器&#xff0c;有的觉得去虚拟机里面安装太麻烦了&#xff0c;电脑会更卡&#xff0c;因此&#xff0c;还不如直接在自己电脑上安装…

客户管理繁,库存盘点难?明道云让你准点下班

文/张占胜 程哲 赵香英 编辑/杜逸敏 一、行业背景 随着疫情的扩散&#xff0c;经济形势日益复杂多变&#xff0c;进销存管理无疑成为了国内企业的焦点。如何利用新时代的先进技术把这一传统管理方式现代化和智能化&#xff0c;已经引起了国内业界的重视&#xff0c;这也是企…

Infleqtion与Morningstar合作探索量子计算的新途径

&#xff08;图片来源&#xff1a;网络&#xff09; 量子计算服务商Infleqtion宣布&#xff0c;将Infleqtion的旗舰量子软件SuperstaQ整合到Morningstar投资和投资组合分析平台Morningstar Direct中。借助SuperstaQ的整合&#xff0c;通过Morningstar的实验室分析模块&#xff…

12.20工作学习记录 力扣 罗马文转数字

每日一题:罗马文转数字 定义两个指针 不断后移 每一次让前一个指针的值累加为sum 最后返回sum 力扣https://leetcode.cn/problems/roman-to-integer/ 最长公共前缀 主要是subString方法 力扣https://leetcode.cn/problems/longest-common-prefix/solutions/现在分词与形容…

【Linux学习】之systemd与systemctl

文章目录一、systemd1. systemd 守护进程管理 Linux 的启动2. systemd 提供的功能:3. systemd 使用单元来管理不同类型的对象。4. 服务单元信息二、systemctl1. systemctl输出中的服务状态2. 列出servera上安装的所以服务单元3. 列出servera上所有活动和不活动的套接字单元4.1 …

Activiti7-任务分配

上面这些已经在流程变量设置的时候学会了 &#xff0c;这里略。 参考&#xff1a;Activiti7-流程变量_ZHOU_VIP的博客-CSDN博客 错别字&#xff1a;认领任务 设计一个流程&#xff0c;设置候选人 错了&#xff0c;应该设置在候选人那里 由于修改了流程设计&#xff0c;需要重…

被勒索后的72 小时“生死时速”

编者按 数字化浪潮蓬勃兴起&#xff0c;企业面临的安全挑战亦日益严峻。 腾讯安全近期将复盘2022年典型的攻击事件&#xff0c;帮助企业深入了解攻击手法和应对措施&#xff0c;完善自身安全防御体系。 本篇是第四期&#xff0c;复盘了一次勒索病毒的紧急应对事件。一旦染上…

【ML】异常检测、PCA、混淆矩阵、调参综合实践(基于sklearn)

【ML】异常检测、PCA、混淆矩阵、调参综合实践&#xff08;基于sklearn&#xff09;加载数据可视化数据异常点检测PCA降维使用KNN进行分类并可视化计算混淆矩阵调节n_neighbors参数找到最优值加载数据 数据集&#xff1a;https://www.kaggle.com/datasets/yuanheqiuye/data-cl…

数据分析之描述性统计

当我们打开一份有十几列&#xff0c;几万行的明细数据时&#xff0c;那种感觉我相信用铺天盖地、头晕目眩来形容是再合适不过了。 面对如此令人崩溃的场景&#xff0c;必须要求我们要求能够快速找到数据的特征。描述性统计正是为此而生&#xff0c;它通过几个简单的分析方法就…

k8s——基于集群部署工具kubeadm部署k8s

角色和IP 通过网络镜像下载和配置k8s 操作系统初始化 关闭防火墙&#xff1a; $ systemctl stop firewalld $ systemctl disable firewalld关闭 selinux&#xff1a; $ sed -i s/enforcing/disabled/ /etc/selinux/config # 永久 $ setenforce 0 # 临时关闭 swap&#xff1…