(十一)手写简单的Spring框架

news2025/1/11 14:19:35

文章目录

  • 第一步:搭建环境
  • 第二步:准备好要管理的Bean
  • 第三步:准备myspring.xml配置文件
  • 第四步:编写MyApplicationContext接口
  • 第五步:编写MyClassPathXmlApplicationContext
  • 第六步:采用Map集合存储Bean
  • 第七步:解析配置文件实例化所有Bean
  • 第八步:给Bean的属性赋值
  • 测试MySpring框架

上一篇:(十)Spring之回顾反射机制

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

第一步:搭建环境

采用Maven方式新建模块:取名MySpring
引入dom4j和jaxen的依赖,因为要使用它解析XML文件,还有junit依赖和log4j2的依赖,方便测试。
log4j2.xml文件放到类路径下。

    <!--dem4j依赖-->
    <dependency>
      <groupId>org.dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>2.1.3</version>
    </dependency>
    <!--jaxen依赖-->
    <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>
    <!--log4j2的依赖-->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.19.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j2-impl</artifactId>
      <version>2.19.0</version>
    </dependency>

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

准备好我们要管理的Bean,这些Bean只是为了方便测试,也就是说这些Bean在将来开发完框架之后是要删除。
创建User类:

public class User {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

创建UserDao类:

public class UserDao {
    public void insert(){
        System.out.println("正在新增用户信息。。。");
    }
}

创建UserDao类:

public class UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

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

注意这个配置文件也是框架使用者提供,将来开发完框架之后是要删除。
使用value给简单属性赋值。使用ref给非简单属性赋值。

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!--这个配置文件也是框架使用者提供-->
    <bean id="user" class="com.myspring.bean.User">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
    </bean>

    <bean id="userDao" class="com.myspring.bean.UserDao">

    </bean>
    <bean id="userService" class="com.myspring.bean.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

第四步:编写MyApplicationContext接口

使用Spring的时候我们第一个要做的是获取Spring容器对象,是一个ApplicationContext接口,所以我们也提供这样一个接口,这个接口将来是要通过getBean方法获取Bean的,所以我们提供这样一个方法。

/**
 * MySpring框架应用上下文
 */
public interface MyApplicationContext {
    /**
     * 根据Bean的名称获取对应的Bean对象
     * @param beanName myspring配置文件中bean标签的id
     * @return 对应的Bean单例对象
     */
    Object getBean(String beanName);
}

第五步:编写MyClassPathXmlApplicationContext

使用Spring的时候获取Spring容器对象是通过new ClassPathXmlApplicationContext得到的,这个类ApplicationContext接口的实现类。要加载类路径下的Spring配置文件。
所以我们创建MyClassPathXmlApplicationContext类继承MyApplicationContext接口。


public class MyClassPathXmlApplicationContext implements MyApplicationContext{
    @Override
    public Object getBean(String beanName) {
        return null;
    }
}

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

Spring是提供三级缓存存储不同时期的Bean,我们不用那么麻烦,采用一个Map集合存储Bean实例。Map集合的key存储bean的id,value存储Bean实例。Map<String,Object>
在MyClassPathXmlApplicationContext类中添加Map<String,Object>属性。
并且在MyClassPathXmlApplicationContext类中添加构造方法,该构造方法的参数接收myspring.xml文件。
同时实现getBean方法。

public class ClassPathXmlApplicationContext implements ApplicationContext{
    /**
     * 存储bean的Map集合
     */
    private Map<String,Object> singletonObjects = new HashMap<>();

    /**
     * 在该构造方法中,解析myspring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。
     * @param resource 配置文件路径(要求在类路径当中)
     */
    public ClassPathXmlApplicationContext(String configLocation) {

    }

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

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

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


public MyClassPathXmlApplicationContext(String configLocation) {
    try {
        //1.使用Dom4j创建reader对象
            SAXReader reader = new SAXReader();
            //2.通过类加载器获取SqlMapper的配置文件输入流
            InputStream myspring_stream = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
            //3.读XML文件,返回一个文档对象document
            Document document = reader.read(myspring_stream);
            //4.对标签进行处理
            //(1)获取所有的bean标签
            List<Node> nodes = document.selectNodes("//bean");
            //(2)对bean标签进行遍历,创建对象曝光到Map集合中
        	nodes.forEach(node -> {
                try {
                    //向下转型,因为Element有更多的方法 Element 继承了 Branch 又继承了 Node ,Node方法比较少。
                    Element beanElt = (Element) node;
                    //获取Bean的id
                    String id = beanElt.attributeValue("id");
                    //获取class属性值
                    String className = beanElt.attributeValue("class");
                    logger.info("beanName:" + id);//使用log4j2记录
                    logger.info("beanClassName:" + className);
                    //通过反射机制创建对象,将其放到Map集合中
                    Class<?> aClass = Class.forName(className);
                    Constructor<?> defaultCon = aClass.getDeclaredConstructor();
                    Object beanObj = defaultCon.newInstance();
                    //将bean曝光,加入Map集合
                    singletonObjects.put(id,beanObj);
                    logger.info(singletonObjects.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
       } catch (Exception e) {
        e.printStackTrace();
    }
}

第八步:给Bean的属性赋值

通过反射机制调用set方法,给Bean的属性赋值。
继续在MyClassPathXmlApplicationContext构造方法中编写代码。

	//(3)再次对bean标签进行遍历,这次主要对bean进行赋值
            nodes.forEach(node -> {
                try {
                    //向下转型,因为Element有更多的方法 Element 继承了 Branch 又继承了 Node ,Node方法比较少。
                    Element beanElt = (Element) node;
                    //获取Bean的id
                    String id = beanElt.attributeValue("id");
                    //获取class属性值
                    String className = beanElt.attributeValue("class");
                    //获取Class
                    Class<?> aClass = Class.forName(className);
                    //获取该bean标签下所有的属性property标签
                    List<Element> propertys = beanElt.elements("property");
                    //遍历property标签
                    propertys.forEach(property ->{
                        try {
                            //获取属性名
                            String propertyName = property.attributeValue("name");
                            logger.info(propertyName);
                            //获取set方法名
                            String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
                            //获取属性类型
                            Field field = aClass.getDeclaredField(propertyName);
                            //获取set方法
                            Method setMethod = aClass.getDeclaredMethod(setMethodName,field.getType());
                            //调用set方法
                            //这里的具体值有可能是value或者ref,需要进行判断
                            String value = property.attributeValue("value");
                            String ref = property.attributeValue("ref");
                            Object actualValue = null;
                            if (value != null) {//简单类型
                                //这里比较麻烦,因为获取到的value的是字符串,需要根据属性类型转换成对应的类型
                                //为了程序简单化,我们定义8种基本类型以及对应的包装类,加上String为简单类型
                                //首先获取属性类型名的简称
                                String propertySimpleName = field.getType().getSimpleName();
                                switch (propertySimpleName){
                                    case "byte":actualValue = Byte.parseByte(value);break;
                                    case "short":actualValue = Short.parseShort(value);break;
                                    case "int":actualValue = Integer.parseInt(value);break;
                                    case "long":actualValue = Long.parseLong(value);break;
                                    case "float":actualValue = Float.parseFloat(value);break;
                                    case "double":actualValue = Double.parseDouble(value);break;
                                    case "boolean":actualValue = Boolean.parseBoolean(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;
                                }
                                setMethod.invoke(singletonObjects.get(id), actualValue);
                            }
                            if (ref != null) {//非简单类型
                                setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));
                            }

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

测试MySpring框架

    @Test
    public void testMySpring(){
        MyApplicationContext myApplicationContext = new MyClassPathXmlApplicationContext("myspring.xml");
        Object user = myApplicationContext.getBean("user");
        System.out.println(user);
        UserService userService = ((UserService) myApplicationContext.getBean("userService"));
        userService.save();
    }

测试完毕,运行成功
请添加图片描述

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

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

相关文章

第十一章 Golang面向对象编程(下)

面向对象编程三大特性 基本介绍 Golang仍然有面向对象编程的继承&#xff0c;封装和多态的特性&#xff0c;只是实现的方式和其他OOP语言不一样。 封装 面向对象编程思想-抽象 我们在前面去定义一个结构体的时候&#xff0c;实际上就是把一类事物共有的属性&#xff08;字段…

Presto 之 explain and explain analyze的实现

一. 前言 本文主要探索在Presto中Explain功能是如何实现的。在Presto中&#xff0c;Explain用法有两种&#xff0c;一种是单纯的explain&#xff0c;此场景只会显示经过RBO优化后的执行计划&#xff0c;但是查询并不会真正地执行。第二种是explain analyze&#xff0c;此场景会…

JVM常用参数

JVM内存相关的几个核心参数 -Xms&#xff1a;Java堆内存初始大小-Xmx&#xff1a;Java堆内存的最大大小-Xmn&#xff1a;Java堆内存中的新生代大小&#xff0c;扣除新生代剩下的就是老年代的内存大小了-XX:PermSize&#xff1a;永久代大小-XX:MaxPermSize&#xff1a;永久代最…

疾控物资管理系统-疾控中心物资管理系统

一、系统概述 东识科技&#xff08;DONWIT&#xff09;疾控中心物资管理系统&#xff08;智物资DW-S300&#xff09;是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对RFID智能仓库进行统一管理、分析的信息化、智能化、规范化的系统。 随着疫情的突…

(STM32)从零开始的RT-Thread之旅--GPIO

上一篇&#xff1a; (STM32)从零开始的RT-Thread之旅--基础项目构建与时钟配置 无论什么开发板&#xff0c;最先调试的肯定是GPIO&#xff0c;一般用来用作指示灯或者按键输入。本篇只是很简单的GPIO应用&#xff0c;没有具体分析RTT框架实现。 首先先创建一个BSP文件夹&…

机器人操作系统ROS(21) jetson nano安装torch tensorflow

安装torch、tensorflow其实跟普通在Linux系统安装没有区别&#xff0c;但是Linux是arch64位的&#xff0c;而jetson是aarch64位的&#xff0c;所以还是不太一样。 另外一个坑是&#xff1a;购买的创乐博的机器人&#xff0c;已经安装ros&#xff0c;但是安装torh的时候需要apt …

使用Spring实现工厂+策略模式

使用Spring实现工厂策略模式 这里使用发短信业务&#xff1a; 不同短信有不同模板但是发送方法都相同只是发送内同不同 1. 定义短信发送策略接口&#xff1a; //策略接口 public interface SmsTemStrategy {public void sendSms(Map<String,String> params); }2.短信…

【Python】Numpy傅里叶变换总结

文章目录简介fft简介 Fourier变换极其逆变换在数学上的定义如下 F(ω)∫−∞∞f(t)e−iωtdtf(t)π2∫−∞∞F(ω)eiωtdωF(\omega)\int^\infty_{-\infty}f(t)e^{-i\omega t}\text dt\\ f(t)\frac{\pi}{2}\int^\infty_{-\infty}F(\omega)e^{i\omega t}\text d\omega F(ω)∫−…

飞行机器人专栏(八)-- AGX Xavier 通信、控制及视觉应用开发

目录 0. Introduction of Jetson Developer kits 1. 硬件对比 Jetson 模组系列 Jetson AGX Orin 系列 Jetson Orin NX 系列 Jetson AGX Xavier 系列 Jetson Xavier NX 系列 Jetson TX2 系列 Jetson Nano 2. 应用场景 1. Introduction of AGX Xavier Taking Perform…

HTML+PHP+MySQL实现新闻列表模块(1+X Web前端开发中级 例题)——初稿

&#x1f4c4;题目要求 阅读下列说明、效果图、MySQL数据库操作和代码&#xff0c;进行动态网页开发&#xff0c;填写&#xff08;1&#xff09;至&#xff08;15&#xff09;代码。&#x1f9e9;说明 该程序为一个html和php混合的新闻列表模块&#xff0c;使用PHP语言&#x…

【深入浅出Spring6】第九期——Spring对事务的支持

因为很多系统都包含事务&#xff0c;所以Spring提供了相关的Api&#xff0c;属于AOP切面编程的二次封装 那么什么是事务&#xff08;Transaction&#xff09;呢&#xff1f; 指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)在一个业务流程中可能需要多条DML联合执…

【Hack The Box】Linux练习-- OpenAdmin

HTB 学习笔记 【Hack The Box】Linux练习-- OpenAdmin &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月22日&#x1f334; &#x…

Flutter组件--OverflowBox、SizedOverflowBox(子组件超出父组件裁剪)

1.OverflowBox介绍 OverflowBox 允许子控件超出父控件的边界。这个特性主要可以用来实现文字或者按钮角标的. OverflowBox构造函数 const OverflowBox({Key? key,this.alignment Alignment.center,this.minWidth,this.maxWidth,this.minHeight,this.maxHeight,Widget? ch…

java面试强基(8)

String、StringBuffer、StringBuilder 的区别&#xff1f; 可变性 ​ String 是不可变的。StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类&#xff0c;在 AbstractStringBuilder 中也是使用字符数组保存字符串&#xff0c;不过没有使用 final 和 private …

Air780E涂鸦云远程开关-LuatOS

Air780E涂鸦云远程开关-LuatOS 涂鸦智能在远程开关和灯控领域可以算是龙头了&#xff0c;今天就来学习一下&#xff0c;如何接入涂鸦云平台 一、涂鸦云准备 注册账号不写了&#xff0c;自己注册账号即可 1、创建产品 点击产品->极速智能化->产品开发页面的创建产品 …

JDK动态代理

可以针对一些不特定的类或者一些不特定的方法进行代理 可以在程序运行时动态变化代理规则 代理类在程序运行时才创建代理模式成为动态代理 代理类并不是在Java代码中定义好的 而是在程序运行时根据在Java代码中指示动态生成的 Proxy JDK动态代理 面向接口 import java.lang.r…

前后端分离页面(从数据库到前端、后端手把手教你如何搭建 -- 功能实现:增加查询)

目录 一、准备条件 前台 后台 二、数据库设计 建议不要导入&#xff0c;导入有乱码的风险&#xff1b;新建查询运行下面代码更快捷。 三、导入前端项目 导入后使用命令行打开黑窗口&#xff0c;测试一下有没有npm -v环境 如果出现命令无效&#xff0c;就使用管理员身份打开…

SpringAOP(1)-spring源码详解(六)

上篇文章说了spring监听器&#xff0c;监听event&#xff0c;先把事件全部存储在多播器&#xff0c;通过publicEvent来触发多播器发送事件&#xff0c;他可以通过代码注解&#xff0c;或者实现接口的方式来实现事件监听&#xff0c;达到解耦和异步的效果。 Spring监听器-sprin…

Jupyter介绍和安装使用

一. 前言&#xff1a; 最近&#xff0c;小熙发现了一个有意思的工具Jupyter notebook&#xff0c;刚上手熟悉了下&#xff0c;特此记录分享。 二. 简介&#xff1a; 简单介绍并说下用途&#xff1a; Jupyter Notebook是一款开源的web应用&#xff0c;支持markdown编辑&#…

Windows为什么使用广播机制更新信息?

很多 Windows 信息通知机制是基于广播消息&#xff0c;例如&#xff0c;DDE&#xff0c;WM_FONTCHANGE 消息和系统设置的修改等。 当我们知道&#xff0c;消息的广播可能导致系统由于某个窗口停止处理消息而导致整个系统停止时&#xff0c;为什么我们还是使用广播呢&#xff1f…