# 手写springioc框架 **Repository Path**: ddfeiyu/framework-easy-springioc ## Basic Information - **Project Name**: 手写springioc框架 - **Description**: 相信所有学过Java的人都应该学习并使用过Spring框架,它是最受欢迎的企业级Java应用程序开发框架,数以千万的来自世界各地的开发人员都在使用 Spring 框架进行程序开发。而Spring的核心是IOC(控制反转)和AOP(面向切面编程)。下面我将会对SpringIOC做详细的介绍并使用反射技术手写一个简单的SpringIOC。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2021-06-28 - **Last Updated**: 2023-07-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 手写springioc框架 #### 介绍 相信所有学过Java的人都应该学习并使用过Spring框架,它是最受欢迎的企业级Java应用程序开发框架,数以千万的来自世界各地的开发人员都在使用 Spring 框架进行程序开发。 而Spring的核心是IOC(控制反转)和AOP(面向切面编程)。 下面我将会对SpringIOC做详细的介绍并使用反射技术手写一个简单的SpringIOC。 #### 软件架构 软件架构说明 1.什么是SpringIOC? 所谓IOC(控制反转),对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象之间的关系。 在传统的程序开发中,如果在一个对象中要使用其他的对象,就必须自己手动new一个,而且在使用完之后还需要将对象进行手动销毁, 这样对象始终会和其他的类藕合起来。 而使用IOC(控制反转),所有的类都会在Spring容器中进行登记,告诉Spring我是什么东西,我需要什么东西,然后Spring会在系统运行到适当的时候, 把你要的东西主动给你,同时也把你交给其他需要你的东西。 所有的类的创建、销毁都由 Spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。 对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被Spring控制,这就叫控制反转。 2.SpringIOC底层实现原理 1.读取bean的XML配置文件 2.使用beanId查找bean配置,并获取配置文件中class的地址 3.使用Java反射技术实例化对象 4.获取属性配置,使用反射技术进行赋值 5.实例化Spring中的依赖注入 详细步骤: 1.利用传入的参数获取xml文件的流,并且利用dom4j解析成Document对象。 2.对于Document对象获取根元素对象后对下面的标签进行遍历,判断是否有符合的beanId。 3.如果找到对应的beanId,相当于找到了一个Element元素,开始创建对象,先获取class属性,然后根据属性值利用反射创建对象。 4.遍历标签下的property标签,并对属性赋值。注意,需要单独处理int,float类型的属性,因为在xml配置中这些属性都是以字符串的形式来配置的,因此需要进行额外的处理。 5.如果属性property标签有ref属性,说明某个属性的值是一个对象,那么根据beanId(ref属性的值)去获取ref对应的对象,再给属性赋值。 6.返回创建的对象,如果没有对应的beanId或者下没有子标签都会返回null。 7.实例化Spring中的依赖注入 定义 BeanDefinition ``` BeanDefinition描述了一个bean实例,该实例具有属性值, package easy.springioc.beans.config; public class BeanDefinition { private String beanId; private Object beanInstance; public BeanDefinition(String beanId, Object beanInstance) { this.beanId = beanId; this.beanInstance = beanInstance; } public String getBeanName() { return toLowerFirstWord(beanInstance.getClass().getSimpleName()); } public String getBeanId() { return beanId; } public Object getBeanInstance() { return beanInstance; } /** * 把字符串的首字母小写 * @param name * @return */ private String toLowerFirstWord(String name){ char[] charArray = name.toCharArray(); charArray[0] += 32; return String.valueOf(charArray); } } ``` 定义RefBeanDefinition ,封装依赖的bean对象 实例: ``` ``` 则RefBeanDefinition的name就是person, beanId就是person1 ``` package easy.springioc.beans.config; public class RefBeanDefinition { private String name; private String beanId; public RefBeanDefinition(String name, String beanId) { this.name = name; this.beanId = beanId; } public String getName() { return name; } public String getBeanId() { return beanId; } } ``` 定义1个map,其中 dependencyBeanMap 定义了bean之间的依赖关系,被依赖的bean用RefBeanDefinition定义,RefBeanDefinition是一个半成品,即没有实例化的bean ``` /** * 未注入 */ protected final Map /*refBeanId 半成品的bean*/> dependencyBeanMap = new ConcurrentHashMap<>(); ``` 定义3个map用于查询实例化的bean,byId查询,byName查询,byType查询 ``` /** * 实例化的bean */ protected final Map beanIdDefinitionMap = new ConcurrentHashMap(256); protected final Map beanNameDefinitionMap = new ConcurrentHashMap(256); protected final Map beanClassDefinitionMap = new ConcurrentHashMap(256); ``` 定义ClassPathXmlApplicationContext用于实现ioc核心功能 ``` package easy.springioc.context.support; import com.alibaba.fastjson.JSON; import easy.springioc.beans.BeansException; import easy.springioc.beans.config.BeanDefinition; import easy.springioc.beans.config.RefBeanDefinition; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { /** * xml配置文件的路径 */ private String xmlPath; public ClassPathXmlApplicationContext(String xmlPath) throws IOException{ this.xmlPath = xmlPath; /** * 1.读取bean的XML配置文件 2.使用beanId查找bean配置,并获取配置文件中class的地址 3.使用Java反射技术实例化对象 4.获取属性配置,使用反射技术进行赋值 */ loadBeanDefinitions(); // 4.实例化Spring中的依赖注入 doAutowired(); } @Override protected void loadBeanDefinitions() throws IOException{ // 1.读取xml配置文件 System.out.println("读取xml配置文件: xmlPath: "+xmlPath); // 1.1创建xml解析器 SAXReader saxReader = new SAXReader(); System.out.println("1.1创建xml解析器"); // 1.2读取xml配置文件 Document read = null; System.out.println("1.2读取xml配置文件"); try { read = saxReader.read(this.getClass().getClassLoader().getResourceAsStream(xmlPath)); } catch (DocumentException e) { e.printStackTrace(); } // 1.3获取xml配置文件的根节点对象() System.out.println("1.3获取xml配置文件的根节点对象()"); Element rootElement = read.getRootElement(); //1.4获取根节点中所有的子节点对象,也就是所有bean对象() System.out.println("1.4获取根节点中所有的子节点对象,也就是所有bean对象()"); List beanElements = rootElement.elements(); Object obj = null; // for (Element beanElement : beanElements) { // 2.使用beanId查找bean配置,并获取配置文件中class的地址(为了与参数beanId区分开,我们命名为beanElementId) System.out.println("2.使用beanId查找bean配置,并获取配置文件中class的地址(为了与参数beanId区分开,我们命名为beanElementId)"); //2.1使用id查找bean配置 String beanElementId = beanElement.attributeValue("id"); // 2.2获取bean对应的Class地址 String beanClazz = beanElement.attributeValue("class"); // 3.使用反射实例化对象 // 3.1获取Class对象 System.out.println(" 3.1获取Class对象"); Class clazz = null; BeanDefinition beanDefinition = null; try { clazz = Class.forName(beanClazz); // 3.2实例化对象 System.out.println(" 3.2实例化对象"); obj = clazz.newInstance(); // 4.获取属性配置,使用反射技术进行赋值 System.out.println(" 4.获取属性配置,使用反射技术进行赋值"); // 4.1获取所有属性 System.out.println(" 4.1获取所有属性"); List fieldElements = beanElement.elements(); beanDefinition = new BeanDefinition(beanElementId, obj); System.out.println("BeanDefinition: "+ JSON.toJSONString(beanDefinition)); for (Element fieldElement : fieldElements) { // String name = fieldElement.attributeValue("name"); String value = fieldElement.attributeValue("value"); String ref = fieldElement.attributeValue("ref"); if (Objects.nonNull(value)){ // 4.2使用反射api为私有属性赋值 System.out.println(" 4.2使用反射api为私有属性赋值"); Field declaredField = clazz.getDeclaredField(name); //忽略访问权限修饰符的安全检查,又称为暴力反射 declaredField.setAccessible(true); declaredField.set(obj, value); }else if (Objects.nonNull(ref)){ // List dependencyBeanList = dependencyBeanMap.get(beanDefinition); if (dependencyBeanList == null){ dependencyBeanList = new ArrayList<>(); } dependencyBeanList.add(new RefBeanDefinition(name,ref)); dependencyBeanMap.put(beanDefinition, dependencyBeanList); // } } System.out.println("BeanDefinition: "+ JSON.toJSONString(beanDefinition)+", 依赖的半成品bean:"+ Arrays.asList(dependencyBeanMap.get(beanDefinition))); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e){ e.printStackTrace(); } catch (SecurityException e){ e.printStackTrace(); } catch (IllegalArgumentException e){ e.printStackTrace(); } catch (IllegalAccessException e){ e.printStackTrace(); } catch (InstantiationException e){ e.printStackTrace(); } beanIdDefinitionMap.put(beanElementId, beanDefinition); beanNameDefinitionMap.put(beanDefinition.getBeanName(), beanDefinition); beanClassDefinitionMap.put(clazz, beanDefinition); } } private void doAutowired() { System.out.println("实例化Spring中的依赖注入"); if (dependencyBeanMap.size() == 0){ return; } Iterator>> dependencyBeanMapIt = dependencyBeanMap.entrySet().iterator(); while (dependencyBeanMapIt.hasNext()){ Map.Entry> e = dependencyBeanMapIt.next(); BeanDefinition beanDefinition = e.getKey(); // 检查依赖对象是否定义以及实例化 List dependencyBeanList = e.getValue(); for (RefBeanDefinition dependencyBean: dependencyBeanList) { String dependencyBeanBeanId = dependencyBean.getBeanId(); BeanDefinition refBeanDefinition = beanIdDefinitionMap.get(dependencyBeanBeanId); if (refBeanDefinition == null){ throw new BeansException("refBeanIdId: "+dependencyBeanBeanId+" Undefined"); } } // 通过反射注入依赖对象实例 Object beanInstance = beanDefinition.getBeanInstance(); Field[] declaredFields = beanInstance.getClass().getDeclaredFields(); List declaredFieldList = Arrays.asList(declaredFields); for (RefBeanDefinition dependencyBean: dependencyBeanList){ String dependencyBeanBeanId = dependencyBean.getBeanId(); BeanDefinition refBeanDefinition = beanIdDefinitionMap.get(dependencyBeanBeanId); Object refBeanDefinitionBeanInstance = refBeanDefinition.getBeanInstance(); String filedName = dependencyBean.getName(); declaredFieldList.stream().filter(declaredField-> Objects.equals(declaredField.getName() , filedName)).forEach(declaredField->{ try { declaredField.setAccessible(true); declaredField.set(beanInstance, refBeanDefinitionBeanInstance); } catch (IllegalAccessException e1) { e1.printStackTrace(); } }); } } } @Override public Object getBeanById(String id) throws IOException { return beanIdDefinitionMap.get(id).getBeanInstance(); } @Override public Object getBeanByName(String beanName) throws IOException { return beanNameDefinitionMap.get(beanName).getBeanInstance(); } @Override public Object getBeansOfType(Class requiredType) throws IOException { return beanClassDefinitionMap.get(requiredType).getBeanInstance(); } } ``` 测试ioc ``` package easy.springioc.ioc; import easy.springioc.context.support.ClassPathXmlApplicationContext; import easy.springioc.entity.HelloWorld; public class TestEasySpringIOC { public static void main(String[] args) throws Exception{ // 读取bean xml配置文件 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("helloioc.xml"); // 按id获取: 根据bean id 获取bean Object getBeanById = applicationContext.getBeanById("world1"); System.out.println("world1: "+ getBeanById); // 按name获取: 根据bean name 获取bean Object getBeanByName = applicationContext.getBeanByName("helloWorld"); System.out.println("helloWorld: "+ getBeanByName); // 按type获取: 根据bean class 获取bean Object getBeansOfType = applicationContext.getBeansOfType(HelloWorld.class); System.out.println("HelloWorld.class: "+ getBeansOfType); // 测试依赖注入 Object world2 = applicationContext.getBeanById("world2"); System.out.println("world2: "+ world2); HelloWorld helloWorldInstance = (HelloWorld) world2; System.out.println(helloWorldInstance.getPerson()); } } ``` #### 安装教程 1. xxxx 2. xxxx 3. xxxx #### 使用说明 1. xxxx 2. xxxx 3. xxxx #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request #### 特技 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)