spring-第十章 模仿手写springIOC

[复制链接]
发表于 2026-1-7 01:18:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

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

×
spring





媒介

本章我们模仿实现springIOC的功能,以此加深对IOC实现原理的明白。
Spring IoC容器的实现原理:工厂模式 + 分析XML + 反射机制。

第一步:创建模块myspring

就是创建一个新的项目,不外创建过程不多说,值得一提的是,我们须要导入以下依赖:


上面的两个包可以让我们可以或许利用xpath格式读取xml文件设置。
第二步:预备好我们要管理的Bean

这里利用User、UserDao、UserService三个类来模仿三层架构,也是我们须要管理的bean。
User

UserDao

UserService

第三步:预备myspring.xml设置文件

我们须要预备一个xml框架设置文件,在内里设置好各个bean的注入关系等。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans>
  3.     <bean id="userBean" class="org.example.powernode.myspring.bean.User">
  4.         <property name="name" value="张三"/>
  5.         <property name="age" value="20"/>
  6.     </bean>
  7.     <bean id="userService" class="org.example.myspring.bean.UserService">
  8.         <property name="userDao" ref="userDaoBean"/>
  9.     </bean>
  10.   
  11.     <bean id="userDaoBean" class="org.example.myspring.bean.UserDao"/>
  12. </beans>
复制代码
第四步:编写ApplicationContext接口

这里编写ApplicationContext接口,原来的spring框架的ApplicationContext接口中getBean()方法是源自BeanFactory()接口,但这里省略写步调,直接将该方法界说在ApplicationContext接口中。
  1. package org.example.powernode.myspring.framework;
  2. public interface ApplicationContext {
  3.     Object getBean(String beanName);
  4. }
复制代码
第五步:编写ClassPathXmlApplicationContext

该类是ApplicationContext接口的实现类,也会实现getBean()方法,是最告急的一步,我们会在构造方法中读取xml中的设置信息,天生对应的bean存放到容器中。然后让getBean()方法可以或许乐成取出实例化完成的bean。
这里就不完备的模仿spring中的三层缓存了,只用一个HashMap聚集即可。
这里我们重要完成以下几步:


  • 确定接纳Map聚集存储Bean
  • 分析设置文件实例化全部Bean,并放进聚会合
  • 给Bean的属性赋值
  1. package org.example.powernode.myspring.framework;
  2. import org.dom4j.Document;
  3. import org.dom4j.Element;
  4. import org.dom4j.Node;
  5. import org.dom4j.io.SAXReader;
  6. import java.io.InputStream;
  7. import java.lang.reflect.Field;
  8. import java.lang.reflect.Method;
  9. import java.util.HashMap;
  10. import java.util.List;
  11. public class ClassPathXmlApplicationContext implements ApplicationContext{
  12. //    创建map集合存储实例化的对象
  13.     private HashMap<String, Object> singletonObjects = new HashMap<>();
  14.     @Override
  15.     public Object getBean(String beanName) {
  16. //        从map集合中取出实例化的对象
  17.         return singletonObjects.get(beanName);
  18.     }
  19. //    解析参数传入的配置文件,然后初始化所有bean对象
  20.     public ClassPathXmlApplicationContext(String configLocation) {
  21.         try {
  22. //            引入dom4j读取器
  23.             SAXReader reader = new SAXReader();
  24. //            根据传入的配置文件类路径读取配置
  25.             InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
  26.             Document document = reader.read(resourceAsStream);
  27. //            使用xpath表达式获取所有的bean对象
  28.             List<Node> nodes = document.selectNodes("//bean");
  29. //            遍历每个bean标签,完成对象实例化操作
  30.             nodes.forEach(node -> {
  31.                 try {
  32. //                    从node类型转换为更具体的Element类型,可用方法更多
  33.                     Element beanElt = (Element) node;
  34. //                    读取bean标签中的id和class属性值
  35.                     String beanName = beanElt.attributeValue("id");
  36.                     String className = beanElt.attributeValue("class");
  37. //                    通过从class属性中读取到的类路径创建Class对象
  38.                     Class<?> aClass = Class.forName(className);
  39. //                    获取构造器创建实例
  40.                     Object o = aClass.getDeclaredConstructor().newInstance();
  41. //                    把创建好的实例存储进map集合,提前"曝光"
  42.                     singletonObjects.put(beanName,o);
  43.                 }catch (Exception e) {
  44.                     e.printStackTrace();
  45.                 }
  46.             });
  47. //            遍历每个bean标签,获取所有的set方法
  48.             nodes.forEach(node -> {
  49.                 try {
  50.                     Element beanElt = (Element) node;
  51. //                    获取bean标签id
  52.                     String beanName = beanElt.attributeValue("id");
  53. //                    获取bean标签类名
  54.                     String className = beanElt.attributeValue("class");
  55. //                    通过类名创建Class对象
  56.                     Class<?> aClass = Class.forName(className);
  57. //                    获取bean标签中的所有property标签
  58.                     List<Element> propertys = beanElt.elements("property");
  59. //                    遍历property标签
  60.                     propertys.forEach(property -> {
  61.                         try {
  62. //                    获取name属性值,也是我们bean的属性名
  63.                             String propertyName = property.attributeValue("name");
  64. //                            通过属性名利用反射机制获取属性对象
  65.                             Field field = aClass.getDeclaredField(propertyName);
  66. //                    拼接得到我们要获取的set方法名
  67.                             String setName="set"+propertyName.substring(0,1).toUpperCase()+propertyName.substring(1);
  68. //                    通过反射传入方法名和参数类型获取set方法对象
  69.                             Method setMethod = aClass.getDeclaredMethod(setName, field.getType());
  70. //                    获取要设置的set参数值
  71.                             String propertyValue = property.attributeValue("value");
  72.                             String ref = property.attributeValue("ref");
  73.                             Object propertyVal = null;
  74.                             if (propertyValue!=null){
  75. //                                简单类型注入,需要判断该简单类型具体的类型来进行把value字符串进行类型转换
  76. //                                支持的简单类型有
  77. //                                byte short int long float double boolean char
  78. //                                Byte Short Integer Long Float Double Boolean Character
  79. //                                String
  80. //                                获取属性类型名
  81.                                 String propertyTypeSimpleName = field.getType().getSimpleName();
  82. //                                根据属性类型名把参数转换为对应的类型
  83.                                 switch (propertyTypeSimpleName){
  84.                                     case "byte": case "Byte":
  85.                                         propertyVal = Byte.valueOf(propertyValue);
  86.                                         break;
  87.                                     case "short": case "Short":
  88.                                         propertyVal = Short.valueOf(propertyValue);
  89.                                         break;
  90.                                     case "int": case "Integer":
  91.                                         propertyVal = Integer.valueOf(propertyValue);
  92.                                         break;
  93.                                     case "long": case "Long":
  94.                                         propertyVal = Long.valueOf(propertyValue);
  95.                                         break;
  96.                                     case "float": case "Float":
  97.                                         propertyVal = Float.valueOf(propertyValue);
  98.                                         break;
  99.                                     case "double": case "Double":
  100.                                         propertyVal = Double.valueOf(propertyValue);
  101.                                         break;
  102.                                     case "boolean": case "Boolean":
  103.                                         propertyVal = Boolean.valueOf(propertyValue);
  104.                                         break;
  105.                                     case "char": case "Character":
  106.                                         propertyVal = propertyValue.charAt(0);
  107.                                         break;
  108.                                     case "String":
  109.                                         propertyVal = propertyValue;
  110.                                         break;
  111.                                 }
  112. //                                使用转换完成的参数调用set方法完成属性注入
  113.                                 setMethod.invoke(singletonObjects.get(beanName),propertyVal);
  114.                             }
  115.                             if (ref!=null){
  116. //                                非简单类型注入,从map集合中取出具体的实例对象注入即可
  117.                                 setMethod.invoke(singletonObjects.get(beanName),singletonObjects.get(ref));
  118.                             }
  119.                         }catch (Exception e) {
  120.                             e.printStackTrace();
  121.                         }
  122.                     });
  123.                 } catch (Exception e) {
  124.                     e.printStackTrace();
  125.                 }
  126.             });
  127.         }catch (Exception e) {
  128.             e.printStackTrace();
  129.         }
  130.     }
  131. }
复制代码
第六步:打包发布

完成后可以创建一个测试类来调用getBean()查察功能是否正常,确认无误可以利用maven打包成jar包,就完成了模仿spring框架编写的全部操纵。

总结

本章我们模仿实现springIOC的功能,以此加深对springIOC的明白。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表