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

手写Spring框架

1. 打包方式采用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.myspringframework</groupId><artifactId>myspring</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>

2. 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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}public class UserDao {public void insert(){System.out.println("mysql数据库正在保存用户信息");}
}public class UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void save(){userDao.insert();}}

3. 准备myspring.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?><beans><bean id="user" class="com.hnlg.myspring.bean.User"><property name="name" value="张三"/><property name="age" value="30"/></bean><bean id="userService" class="com.hnlg.myspring.bean.UserService"><property name="userDao" ref="userDaoBean"/></bean><bean id="userDaoBean" class="com.hnlg.myspring.bean.UserDao"/>
</beans>

4. 日志文件log4j2.xml

<?xml version="1.0" encoding="UTF-8"?><configuration><appenders><!--输出日志信息到控制台--><console name="spring6log" target="SYSTEM_OUT"><!--控制日志输出的格式--><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/></console></appenders><loggers><!--level指定日志级别,从低到高的优先级:(级别越高,输出信息越精简)ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF--><root level="INFO"><appender-ref ref="spring6log"/></root></loggers>
</configuration>

5. 编写ApplicationContext接口

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

public interface ApplicationContext {/*** 根据bean的名称获取对应的bean对象。* @param beanName myspring配置文件中bean标签的id。* @return 对应的单例bean对象。*/Object getBean(String beanName);}

6. 编写ClassPathXmlApplicationContext

public class ClassPathXmlApplicationContext implements ApplicationContext{@Overridepublic Object getBean(String beanId) {return null;}
}

7. 采用Map集合存储Bean

采用Map集合存储Bean实例。Map集合的key存储bean Id,value存储Bean实例。

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

在ClassPathXmlApplicationContext类中添加构造方法,该构造方法的参数接收myspring.xml文件。同时实现getBean()方法。

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

8. 解析配置文件实例化所有Bean

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

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();}
}

9. 给Bean的属性赋值

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

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

public class ClassPathXmlApplicationContext implements ApplicationContext{/*** 存储bean的Map集合*/private Map<String,Object> beanMap = new HashMap<>();/*** 在该构造方法中,解析myspring.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);}
}

http://www.xdnf.cn/news/1458883.html

相关文章:

  • 前端跨域终极指南:3 种优雅解决方案 + 可运行 Demo
  • 解密注意力机制:为何它能在Transformer中实现高效并行计算?
  • STM32G4 速度环开环,电流环闭环 IF模式建模
  • 如何在Linux上部署1Panel面板并远程访问内网Web端管理界面
  • Kafka 开启 SASL_PLAINTEXT 双监听器认证(内网/外网)
  • 如何减少文档冗余和重复劳动
  • vite_react 插件 find_code 最终版本
  • MVCC是如何工作的?
  • bash自带的切片操作
  • 解锁“桐果云”的全链路能力矩阵,技术人必看的企业级数据应用方案
  • 阿里云轻量应用服务器部署WordPress与配置SSL 证书
  • 英飞凌ASIL-D级无刷电机驱动芯片TLE9189守护汽车安全
  • 第三方网站测试:WEB安全测试中DOM型XSS漏洞的检测
  • [Windows] PDF工具箱 PDF24 Creator 11.28.0
  • 为什么ApiFox的分页查询的返回Vo的数据没有全部展示? 只展示了返回有数据的?没有数据的为什么不展示?
  • N个代码片段之封装继承与多态
  • Docker(①安装)
  • 视频小浮窗Pip的实现
  • 【RNN-LSTM-GRU】第四篇 GRU门控循环单元:LSTM的高效替代者与实战指南
  • 力扣14:最长公共前缀
  • 【系统架构设计(14)】项目管理下:软件质量与配置管理:构建可靠软件的基础保障
  • 出海马来西亚,九识智能携手ALS共同启动首个自动驾驶物流车公开道路试运行
  • 详细讲解pyspark中dsl格式进行大数据开发中的的所有编程情况
  • PCB中的CAF深度剖析与对策研究
  • 【109】基于51单片机自动感应迎宾门【Proteus仿真+Keil程序+报告+原理图】
  • 【数学建模学习笔记】机器学习分类:KNN分类
  • Full cycle of a machine learning project|机器学习项目的完整周期
  • 9.4C++——继承
  • MySQL命令--备份和恢复数据库的Shell脚本
  • C++工程实战入门笔记11-三种初始化成员变量的方式