手写Spring框架

打印 上一主题 下一主题

主题 918|帖子 918|积分 2754

1. 手写Spring框架

@
目次

每博一文案
  1. 特别喜欢一种人
  2. 他们的知识储备和信息密度,都远远高于我
  3. 但还是愿意认真听我说,那些没有营养的废话
  4. 我始终觉得,温柔浪漫跟博学,是人身上最难得的特性
  5. 懂得向下兼容的人,走到哪里都是宝藏
  6. 太喜欢哪些优秀,却不带优越感的人相处了
复制代码
2. 反射机制的回顾

我们知道框架一样平常是由 设计模式+注解+反射 来实现,并举行运用开发的。
所以我们要手写 Spring 框架,就需要先来回顾,回顾Java当中的反射机制,这里是简单的回顾反射 。关于反射机制更加详细的内容,大家可以移步至✏️✏️✏️ Java “框架 = 注解 + 反射 + 设计模式” 之 反射详解-CSDN博客
我们知道,想要调用一个方法,就需要明确知道该方法的四个要素:

  • 调用的是哪个对象的
  • 哪个方法
  • 该方法传什么参数
  • 有无返回值,有返回值的话,又该返回什么值
为了简单的回顾我们的反射机制,下面我们举行一个简单的任务。完成下面的需求
首先你知道以下这几条信息
  1. 假设你现在以下信息:
  2. 1.有这样一个类,类名叫做:com.rainbowsea.reflect.User
  3. 2.这个类符合javabean规范,属性私有化,对外提供公开的setter和getter方法。
  4. 3.你还知道这个类当中有一个属性,属性的名字叫做 age
  5. 4.并且你还知道age属性的类型是 int 类型
  6. 请使用反射机制调用set()方法,给 User 对象的age 属性赋值(age 属性赋值为 20 )
复制代码
  1. package com.rainbowsea.reflect;
  2. public class User {
  3.     private String name;
  4.     private int age;
  5.     public String getName() {
  6.         return name;
  7.     }
  8.     public void setName(String name) {
  9.         this.name = name;
  10.     }
  11.     public int getAge() {
  12.         return age;
  13.     }
  14.     public void setAge(int age) {
  15.         this.age = age;
  16.     }
  17.     @Override
  18.     public String toString() {
  19.         return "User{" +
  20.                 "name='" + name + '\'' +
  21.                 ", age=" + age +
  22.                 '}';
  23.     }
  24. }
复制代码
下面通过反射机制,获取到为 age 属性值举行赋值操纵。

  1. package com.rainbowsea.test;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. public class Test2 {
  6.     /*
  7.     需求:
  8.     假设你现在以下信息:
  9.     1.有这样一个类,类名叫做:com.powernode.reflect.User
  10.     2.这个类符合javabean规范,属性私有化,对外提供公开的setter和getter方法。
  11.     3.你还知道这个类当中有一个属性,属性的名字叫做 age
  12.     4.并且你还知道age属性的类型是 int 类型
  13.    请使用反射机制调用set()方法,给 User 对象的age 属性赋值
  14.      */
  15.     public static void main(String[] args) {
  16.         String className = "com.rainbowsea.reflect.User";
  17.         String propertyName = "age";
  18.         try {
  19.             // 通过 反射机制调用setAge(int) 方法
  20.             // 获取到对应的类
  21.             Class<?> clazz = Class.forName(className);
  22.             // 获取对应的方法
  23.             // 方法名: 这里我们知道set 方法格式为: setAge() set+属性名的首字母大小+后面属性名的小写字母
  24.             String setMethodName = "set" + propertyName.toUpperCase().charAt(0)+ propertyName.substring(1);
  25.             // 获取对应的方法名:
  26.             // 我们知道的属性名就可以,根据属性名获取属性类型
  27.             // 下面这里获取到的是一个完整的:private int com.rainbowsea.reflect.User.age 类型名
  28.             Field field = clazz.getDeclaredField(propertyName);
  29.             // field.getType() 获取到属性的类型,是个简单的类型 int
  30.             Method declaredMethod = clazz.getDeclaredMethod(setMethodName, field.getType());
  31.             //System.out.println(field.getType());
  32.             
  33.             // 准备对象
  34.             Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
  35.             Object obj = declaredConstructor.newInstance();
  36.             // 调用set()方法赋值, 没有返回值(调用 obj 的对象中的,declaredMethod()方法,参数是 30,没有返回值)
  37.             declaredMethod.invoke(obj,30);
  38.             System.out.println(obj);
  39.         } catch (Exception e) {
  40.             e.printStackTrace();
  41.             throw new RuntimeException(e);
  42.         }
  43.     }
  44. }
复制代码

成功了,哪我们反射机制就算简单回顾完了,该上正菜了——》手写 Spring 框架
3. 开始手写 Spring 框架

Spring IoC容器的实现原理:工厂模式 + 剖析XML + 反射机制
我们给自己的框架起名为:myspring(我的春天)
3.1 第一步:使用 IDE 创建模块myspring

这里我们接纳 IDE 当中的Maven 创建一个 名为 myspring的 Module。

注意:这里是 org.myspringframework  我们现在的角色是一个spring开发者职员。所以我们的包名的命名格式还是要规定一下的。

打包方式接纳 jar,并且还需要引入 dom4jjaxen的依赖,由于要使用它剖析XML文件,还有junit依赖。(举行测试)。有上面三个就可以了,这里的话,我再引入一个  log4j2 的依赖,用于日志信息的显示记录。由于我们这里是,自己手写 Spring 框架,所以就不要再引入 Spring的 jar 包内容了。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5.     <modelVersion>4.0.0</modelVersion>
  6.     <groupId>com.rainbowsea</groupId>
  7.     <artifactId>myspring</artifactId>
  8.     <version>1.0-SNAPSHOT</version>
  9.     <packaging>jar</packaging>
  10.     <properties>
  11.         <maven.compiler.source>17</maven.compiler.source>
  12.         <maven.compiler.target>17</maven.compiler.target>
  13.     </properties>
  14.     <dependencies>
  15.         <dependency>
  16.             <groupId>org.dom4j</groupId>
  17.             <artifactId>dom4j</artifactId>
  18.             <version>2.1.3</version>
  19.         </dependency>
  20.         <dependency>
  21.             <groupId>jaxen</groupId>
  22.             <artifactId>jaxen</artifactId>
  23.             <version>1.2.0</version>
  24.         </dependency>
  25.         <dependency>
  26.             <groupId>junit</groupId>
  27.             <artifactId>junit</artifactId>
  28.             <version>4.13.2</version>
  29.             <scope>test</scope>
  30.         </dependency>
  31.         <dependency>
  32.             <groupId>org.apache.logging.log4j</groupId>
  33.             <artifactId>log4j-core</artifactId>
  34.             <version>2.19.0</version>
  35.         </dependency>
  36.         <dependency>
  37.             <groupId>org.apache.logging.log4j</groupId>
  38.             <artifactId>log4j-slf4j2-impl</artifactId>
  39.             <version>2.19.0</version>
  40.         </dependency>
  41.     </dependencies>
  42. </project>
复制代码
3.2 第二步:预备好我们要管理的Bean

预备好我们要管理的Bean(这些Bean在将来开发完框架之后是要删除的
注意包名,不要用 org.myspringframework包了,由于这些Bean不是框架内置的,是将来使用我们写好 Spring 框架方便我们后续测试,基于便于我们手写 Spring 框架举行思考,所以这里我就定义为了。com.rainbowsea.myspring.bean  大家可以自行定义
一共定义了 三个 Bean :User,UserService,UserDao
  1. package com.rainbowsea.myspring.bean;
  2. public class User {
  3.     private String name;
  4.     private int age;
  5.     public void setName(String name) {
  6.         this.name = name;
  7.     }
  8.     public void setAge(int age) {
  9.         this.age = age;
  10.     }
  11.     @Override
  12.     public String toString() {
  13.         return "User{" +
  14.                 "name='" + name + '\'' +
  15.                 ", age=" + age +
  16.                 '}';
  17.     }
  18. }
复制代码
  1. package com.rainbowsea.myspring.bean;
  2. public class UserDao {
  3.     public void insert() {
  4.         System.out.println("数据库插入数据");
  5.     }
  6. }
复制代码
  1. package com.rainbowsea.myspring.bean;
  2. public class UserService {
  3.     private UserDao userDao;
  4.     public void setUserDao(UserDao userDao) {
  5.         this.userDao = userDao;
  6.     }
  7.     public void save() {
  8.         userDao.insert();
  9.     }
  10. }
复制代码
3.3 第三步:预备myspring.xml配置文件

将来在框架开发完毕之后,这个文件也是要删除的。由于这个配置文件的提供者应该是使用这个框架的程序员。这里我是同样也是为了,用于验证测试,我们手写额Spring框架的。文件名随意,我们这里叫做:myspring.xml。文件放在类路径当中即可,我们这里把文件放到类的根路径下。使用value给简单属性赋值。使用ref给非简单属性赋值。

由于上面我配置了引入一个  log4j2 的依赖,用于日志信息的显示记录。所以这里,我们还需配置一个关于 log4j2 的配置文件。

3.4 第四步:编写ApplicationContext接口

ApplicationContext 接口中提供一个getBean()方法,通过该方法可以获取Bean对象。
注意包名:这个接口就是myspring框架中的一员了。

3.5 第五步:编写ClassPathXmlApplicationContext

ClassPathXmlApplicationContext 是 ApplicationContext 接口的实现类。该类从类路径当中加载myspring.xml 配置文件。
  1. package org.myspringframework.core;
  2. public class ClassPathXmlApplicationContext implements ApplicationContext {
  3.     @Override
  4.     public Object getBean(String beanName) {
  5.         return singletonObjects.get(beanName);
  6.     }
  7. }
复制代码
3.6 第六步:确定接纳Map聚集存储Bean

确定接纳Map聚集存储Bean实例。Map聚集的key存储 beanId,value存储Bean实例。Map
在ClassPathXmlApplicationContext类中添加 Map属性。
并且在ClassPathXmlApplicationContext类中添加构造方法,该构造方法的参数接收myspring.xml文件。
同时实现getBean方法。
  1. package org.myspringframework.core;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class ClassPathXmlApplicationContext implements ApplicationContext {
  5.     // 解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中
  6.     private Map<String, Object> singletonObjects = new HashMap<>();
  7.     }
  8.     @Override
  9.     public Object getBean(String beanName) {
  10.         return singletonObjects.get(beanName);
  11.     }
  12. }
复制代码
3.7 第七步:剖析配置文件,并实例化所有Bean

在 ClassPathXmlApplicationContext 的构造方法中剖析配置文件,获取所有bean的类名,通过反射机制调用无参数构造方法创建Bean。并且将Bean对象存放到Map聚集中。
增补:
首先我们需要,获取到一个名为 SAXReader(这是dom4j 剖析xml 文件的核心对象) 的对象以及Document ,用来读取我们当中的myspring.xml'所含的 bean 的配置信息。这两个对象可以读取到 xml 配置文件的信息。


同时需要:注意的是:在 java 当中一个 ”//“ 才体现为一个 / 。

注意一个向下转型:向下转型的目标是为了使用Element接口更加丰富的方法。
该 Element 可以获取到  标签当中的 id 的属性值,和 标签当中的 class 属性的值




  1. package org.myspringframework.core;
  2. import org.dom4j.Document;
  3. import org.dom4j.Element;
  4. import org.dom4j.Node;
  5. import org.dom4j.io.SAXReader;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import java.io.InputStream;
  9. import java.lang.reflect.Constructor;
  10. import java.lang.reflect.Field;
  11. import java.lang.reflect.Method;
  12. import java.util.HashMap;
  13. import java.util.List;
  14. import java.util.Map;
  15. public class ClassPathXmlApplicationContext implements ApplicationContext {
  16.     // 解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中
  17.     private Map<String, Object> singletonObjects = new HashMap<>();
  18.     /**
  19.      * 解析myspring的配置文件,然后初始化所有的Bean对象
  20.      *
  21.      * @param configLocation spring配置文件的路径,注意:使用的是ClassPathXmlApplicationContext ,所以
  22.      *                       配置文件应当放到类路径下
  23.      */
  24.     public ClassPathXmlApplicationContext(String configLocation) {
  25.         // 解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中
  26.         // 这是dom4j 解析xml 文件的核心对象
  27.         SAXReader saxReader = new SAXReader();
  28.         Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);
  29.         // 获取一个输入流,指向配置文件
  30.         InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
  31.         try {
  32.             // 读文件
  33.             Document document = saxReader.read(in);
  34.             // 获取到所有的 <bean>标签
  35.             // 注意要两个//,才能表示一个 /
  36.             List<Node> nodes = document.selectNodes("//bean");
  37.             // 遍历 bean标签
  38.             nodes.forEach(node -> {
  39.                 //System.out.println(node);
  40.                 // 向下转型的目的是为了使用Element接口更加丰富的方法
  41.                 Element beanElt = (Element) node;
  42.                 // 获取 <bean>标签当中的id的属性
  43.                 String id = beanElt.attributeValue("id");
  44.                 // 获取 <bean>标签当中的 class 属性
  45.                 String className = beanElt.attributeValue("class");
  46.                 //logger.info("beanName" + id);
  47.                 //logger.info("beanClassName " + className);
  48.                 try {
  49.                     // 通过反射机制创建对象,将其放到Map集合中,提前曝光
  50.                     // 获取class对象
  51.                     Class<?> aClass = Class.forName(className);
  52.                     // 获取无参构造方法实例化Bean
  53.                     Constructor<?> declaredCon = aClass.getDeclaredConstructor();
  54.                     // 调用无参构造方法实例化Bean
  55.                     Object bean = declaredCon.newInstance();
  56.                     // 将Bean 曝光,加入 Map集合
  57.                     singletonObjects.put(id, bean);
  58.                     // 记录日志
  59.                     logger.info(singletonObjects.toString());
  60.                 } catch (Exception e) {
  61.                 e.printStackTrace();
  62.             }
  63.         });
  64.     } catch (Exception e) {
  65.         e.printStackTrace();
  66.     }
  67. }
  68.     @Override
  69.     public Object getBean(String beanName) {
  70.         return singletonObjects.get(beanName);
  71.     }
  72. }
复制代码
3.8 第八步:测试可否获取到Bean

  1. package com.rainbowsea.myspring.test;
  2. import com.rainbowsea.myspring.bean.UserService;
  3. import org.junit.Test;
  4. import org.myspringframework.core.ApplicationContext;
  5. import org.myspringframework.core.ClassPathXmlApplicationContext;
  6. public class MySpringTest {
  7.     @Test
  8.     public void testMySpring() {
  9.         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml");
  10.         Object user = applicationContext.getBean("user");
  11.         System.out.println(user);
  12.     }
  13. }
复制代码
我们的 myspring.xml 配置文件当中,有三个 bean 对象的配置信息,所以下面应当显示三个 Bean的实例对象。如下显示结果,我们可以看出,是成功获取到了 myspring.xml 配置文件当中所有的 Bean 对象了。

通过测试Bean已经实例化成功了,属性的值是null,这是我们能够想到的,究竟我们调用的是无参数构造方法,所以属性都是默认值。
下一步就是我们应该如何给Bean的属性赋值了。
3.9 第九步:给Bean的属性赋值

通过反射机制调用set方法,给Bean的属性赋值。
继承在ClassPathXmlApplicationContext构造方法中编写代码。
增补分析:
我们需要,再次重新把所有的 Bean 标签遍历一次,这一次重要是给对象属性赋值的

获取到 获取该标签下的所有的属性为 标签,由于 在  标签当中存放着,我们对属性赋值要的确切的值。

赋值,这里我们是通过 set 方法举行赋值的,这个简单,我们可以通过反射(对象,方法名,方法参数类型)获取到方法的对象,又可以通过属性名,获取到属性的类型。

这里最难的部分就是,在我们的 在  标签当中存放着,我们对属性赋值要的确切的值。但是这个值有两种:简单类型 value 的值,复杂类型 ref 的值。复杂类型就是引用类型,这个我们以及在曝光的时候,就存储到Map当中去了,我们只需要通过对应的beanName 从 Map当前取出来,再通过反射机制调用set方法举行一个赋值。

简单类型的赋值操纵:
简单类型的复杂点,就是一个转换,我们再 myspring.xml 配置文件当中的,属性值都是以字符串的形式存在的,而我们的现实属性的类型,大概是 int,char,double 等等类型的,String 字符串类型不可以直接赋值到其他 int,char 类型当中去,所以我们就需要对我们所赋值的属性举行一个判断,假如该类型是 int 类型,我们就需要将 String 字符串类型转换为我们所需要的 int 类型。
这里我们分析一下:
  1. 我们myspring框架说明以下,我们只支持这些类型为简单类型
  2. byte short int long float double boolean char
  3. Byte Short Intger Long Float Double Boolean Character
  4. String
  5. */
复制代码



完备代码:
  1. package org.myspringframework.core;
  2. import org.dom4j.Document;
  3. import org.dom4j.Element;
  4. import org.dom4j.Node;
  5. import org.dom4j.io.SAXReader;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import java.io.InputStream;
  9. import java.lang.reflect.Constructor;
  10. import java.lang.reflect.Field;
  11. import java.lang.reflect.Method;
  12. import java.util.HashMap;
  13. import java.util.List;
  14. import java.util.Map;
  15. public class ClassPathXmlApplicationContext implements ApplicationContext {
  16.     // 解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中
  17.     private Map<String, Object> singletonObjects = new HashMap<>();
  18.     /**
  19.      * 解析myspring的配置文件,然后初始化所有的Bean对象
  20.      *
  21.      * @param configLocation spring配置文件的路径,注意:使用的是ClassPathXmlApplicationContext ,所以
  22.      *                       配置文件应当放到类路径下
  23.      */
  24.     public ClassPathXmlApplicationContext(String configLocation) {
  25.         // 解析myspring.xml文件,然后实例化Bean,将Bean存放到singletonObjects集合当中
  26.         // 这是dom4j 解析xml 文件的核心对象
  27.         SAXReader saxReader = new SAXReader();
  28.         Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);
  29.         // 获取一个输入流,指向配置文件
  30.         InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
  31.         try {
  32.             // 读文件
  33.             Document document = saxReader.read(in);
  34.             // 获取到所有的 <bean>标签
  35.             // 注意要两个//,才能表示一个 /
  36.             List<Node> nodes = document.selectNodes("//bean");
  37.             // 遍历 bean标签
  38.             nodes.forEach(node -> {
  39.                 //System.out.println(node);
  40.                 // 向下转型的目的是为了使用Element接口更加丰富的方法
  41.                 Element beanElt = (Element) node;
  42.                 // 获取 <bean>标签当中的id的属性
  43.                 String id = beanElt.attributeValue("id");
  44.                 // 获取 <bean>标签当中的 class 属性
  45.                 String className = beanElt.attributeValue("class");
  46.                 //logger.info("beanName" + id);
  47.                 //logger.info("beanClassName " + className);
  48.                 try {
  49.                     // 通过反射机制创建对象,将其放到Map集合中,提前曝光
  50.                     // 获取class对象
  51.                     Class<?> aClass = Class.forName(className);
  52.                     // 获取无参构造方法实例化Bean
  53.                     Constructor<?> declaredCon = aClass.getDeclaredConstructor();
  54.                     // 调用无参构造方法实例化Bean
  55.                     Object bean = declaredCon.newInstance();
  56.                     // 将Bean 曝光,加入 Map集合
  57.                     singletonObjects.put(id, bean);
  58.                     // 记录日志
  59.                     //logger.info(singletonObjects.toString());
  60.                     //System.out.println(singletonObjects.toString());
  61.                 } catch (Exception e) {
  62.                     e.printStackTrace();
  63.                     throw new RuntimeException(e);
  64.                 }
  65.             });
  66.             // 再次重新把所有的 Bean 标签遍历一次,这一次主要是给对象属性赋值的
  67.             nodes.forEach(node -> {
  68.                 Element beanElt = (Element) node;
  69.                 // 获取 <bean>标签当中的 id 属性
  70.                 String id = beanElt.attributeValue("id");
  71.                 // 获取<bean>标签当中的 class 属性的值
  72.                 String className = beanElt.attributeValue("class");
  73.                 // 获取Class 对象通过全限定类名+反射机制
  74.                 try {
  75.                     Class<?> aClass = Class.forName(className);
  76.                     // 获取该<bean>标签下的所有的属性为 <property>标签
  77.                     List<Element> propertys = beanElt.elements("property");
  78.                     // 遍历<property> 的所有的属性标签
  79.                     propertys.forEach(property -> {
  80.                         // 获取 <property> 标签下的name 属性名下的值
  81.                         String propertyName = property.attributeValue("name");
  82.                         //logger.info(" <property> 标签下的name 属性名 " + propertyName);
  83.                         // 获取 <property> 标签下的name 属性名下的值
  84.                         // 获取 set 方法名 set+属性名第一个单词大写
  85.                         String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
  86.                         try {
  87.                             //获取 set 方法
  88.                             // 获取属性类型,通过属性名,可以获取到属性类型
  89.                             Field declaredField = aClass.getDeclaredField(propertyName);
  90.                             // 获取set方法
  91.                             Method setMethod = aClass.getDeclaredMethod(setMethodName, declaredField.getType());
  92.                             // 调用set方法(set方法没有返回值)
  93.                             // 获取 <property> 标签当中的 value 或者是 ref 的具体的值
  94.                             String value = property.attributeValue("value");
  95.                             String ref = property.attributeValue("ref");
  96.                             Object actualValue = null; // 真值
  97.                             if (value != null) {
  98.                                 // 说明在<property> 标签当中  这个值是简单类型
  99.                                 /*
  100.                                 我们myspring框架说明以下,我们只支持这些类型为简单类型
  101.                                 byte short int long float double boolean char
  102.                                 Byte Short Intger Long Float Double Boolean Character
  103.                                 String
  104.                                  */
  105.                                 // 获取属性类型名
  106.                                 String propertyTypeSimpleName = declaredField.getType().getSimpleName();
  107.                                 switch (propertyTypeSimpleName) {
  108.                                     case "byte":
  109.                                         actualValue = Byte.parseByte(value);
  110.                                         break;
  111.                                     case "short":
  112.                                         actualValue = Short.parseShort(value);
  113.                                         break;
  114.                                     case "int":
  115.                                         actualValue = Integer.parseInt(value);
  116.                                         break;
  117.                                     case "long":
  118.                                         actualValue = Long.parseLong(value);
  119.                                         break;
  120.                                     case "float":
  121.                                         actualValue = Float.parseFloat(value);
  122.                                         break;
  123.                                     case "double":
  124.                                         actualValue = Double.parseDouble(value);
  125.                                         break;
  126.                                     case "boolean":
  127.                                         actualValue = Boolean.parseBoolean(value);
  128.                                         break;
  129.                                     case "char":
  130.                                         actualValue = value.charAt(0);
  131.                                         break;
  132.                                     case "Byte":
  133.                                         actualValue = Byte.valueOf(value);
  134.                                         break;
  135.                                     case "Short":
  136.                                         actualValue = Short.valueOf(value);
  137.                                         break;
  138.                                     case "Integer":
  139.                                         actualValue = Integer.valueOf(value);
  140.                                         break;
  141.                                     case "Long":
  142.                                         actualValue = Long.valueOf(value);
  143.                                         break;
  144.                                     case "Float":
  145.                                         actualValue = Float.valueOf(value);
  146.                                         break;
  147.                                     case "Double":
  148.                                         actualValue = Double.valueOf(value);
  149.                                         break;
  150.                                     case "Boolean":
  151.                                         actualValue = Boolean.valueOf(value);
  152.                                         break;
  153.                                     case "Character":
  154.                                         // char 仅仅只是一个字符,所以要获取第一个字符
  155.                                         actualValue = value.charAt(0);
  156.                                         break;
  157.                                     case "String":
  158.                                         actualValue = value;
  159.                                         break;
  160.                                 }
  161.                                 setMethod.invoke(singletonObjects.get(id), actualValue);
  162.                             }
  163.                             if (ref != null) {
  164.                                 // 说明这个值是非简单类型
  165.                                 // 因为是非简单类型,那么它提前曝光的,就存放到了集合当前去了。
  166.                                 setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));
  167.                             }
  168.                         } catch (Exception e) {
  169.                             throw new RuntimeException(e);
  170.                         }
  171.                     });
  172.                 } catch (ClassNotFoundException e) {
  173.                     throw new RuntimeException(e);
  174.                 }
  175.             });
  176.         } catch (Exception e) {
  177.             e.printStackTrace();
  178.             throw new RuntimeException(e);
  179.         }
  180.     }
  181.     @Override
  182.     public Object getBean(String beanName) {
  183.         return singletonObjects.get(beanName);
  184.     }
  185. }
复制代码
3.10 第十步:测试是否能够正常赋值成功

注意: 不要导错包了,我们这里导入的是,我们自己手写的Spring框架的包。


3.11 第十一步:打包发布

将多余的类以及配置文件删除,当然,我们这里并不是真正的开发 Spring 框架,所以不删除也是没有关系的(只是需要注意不要导错jar 包了)。这里我们使用maven打包发布。关于 Maven 的使用上的内容,想要相识更多的。大家可以移步:✏️✏️✏️ Maven_ChinaRainbowSea的博客-CSDN博客。各种细节内容都是截屏标志了的,这个大家放心。




3.12 第十二步: 站在程序员角度使用我们自己手写的 spring 的 myspring框架

第一步: 我们新建一个名为: myspring-text 的模块,举行一个我们手写的Spring 的框架的测试。

第二步: 引入我们自己编写的Spring 框架,不要引错了。


第三步: 编写预备一些Bean 对象用于测试,我们自己编写的 Spring 框架


  1. package com.rainbowsea.myspring.bean;
  2. public class Vip {
  3.     private String name;
  4.     private int age;
  5.     private double height;
  6.     public void setName(String name) {
  7.         this.name = name;
  8.     }
  9.     public void setAge(int age) {
  10.         this.age = age;
  11.     }
  12.     public void setHeight(double height) {
  13.         this.height = height;
  14.     }
  15.     @Override
  16.     public String toString() {
  17.         return "Vip{" +
  18.                 "name='" + name + '\'' +
  19.                 ", age=" + age +
  20.                 ", height=" + height +
  21.                 '}';
  22.     }
  23. }
复制代码
  1. package com.rainbowsea.myspring.bean;
  2. public class OrderDao {
  3.     public void insert() {
  4.         System.out.println("插入数据");
  5.     }
  6. }
复制代码
  1. package com.rainbowsea.myspring.bean;
  2. public class OrderService {
  3.     private OrderDao orderDao;
  4.     public void setOrderDao(OrderDao orderDao) {
  5.         this.orderDao = orderDao;
  6.     }
  7.     public void generate() {
  8.         orderDao.insert();
  9.     }
  10. }
复制代码
第四步: 运行,测试,我们编写的Spring 的框架是否成功。
注意:导入的是我们自己编写的Spring 框架。不要导错了。

从我们的运行结果上看,我们编写的Spring 框架是没有问题的。
4. 总结:


  • 框架一样平常是由 设计模式+注解+反射 来实现,并举行运用开发的。而这里我们的Spring IoC容器的实现原理:工厂模式 + 剖析XML + 反射机制
  • 我们知道,想要调用一个方法,就需要明确知道该方法的四个要素:

    • 调用的是哪个对象的
    • 哪个方法
    • 该方法传什么参数
    • 有无返回值,有返回值的话,又该返回什么值

  • 引入 dom4jjaxen的依赖,由于要使用它剖析XML文件, SAXReader(这是dom4j 剖析xml 文件的核心对象) 的对象以及Document ,用来读取我们当中的myspring.xml'所含的 bean 的配置信息。这两个对象可以读取到 xml 配置文件的信息。

  • 编写Spring 框架比较难的点,就是:简单类型的复杂点,就是一个转换,我们再 myspring.xml 配置文件当中的,属性值都是以字符串的形式存在的,而我们的现实属性的类型,大概是 int,char,double 等等类型的,String 字符串类型不可以直接赋值到其他 int,char 类型当中去,所以我们就需要对我们所赋值的属性举行一个判断,假如该类型是 int 类型,我们就需要将 String 字符串类型转换为我们所需要的 int 类型。
  • 注意:我们手写的Spring 框架不并不是完完全全按照,真正的Spring框架来编写的,这是大部分思路是一致的,对应差别需要上的处理大概不太一致,这一点希望大家可以明白。
  • 我们手写Spring 框架是为了,更好的理解Spring 框架的原理,从而不惧怕Spring的使用,在使用上更加的机动,得心易手。
5. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继承在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

我可以不吃啊

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表