行业知识
【Spring】模拟 Spring 框架
Spring IoC 容器���实现原理:工厂模式 + 解析XML + 反射机制
我们给自己的框架起名为:mySpring(我的春天)
一、创建 mySpring 模块
引入dom4j 和 jaxen 的依赖,因为要使用它解析XML文件,还有 junit 依赖
4.0.0 org.myspringframework mySpring 1.0.0 8 8 org.dom4j dom4j 2.1.3 jaxen jaxen 1.2.0 junit junit 4.13.2 test
二、准备要管理的 Bean
准备好我们要管理的Bean(这些Bean在将来开发完框架之后是要删除的)
注意包名,不要用org.myspringframework包,因为这些Bean不是框架内置的。是将来使用我们框架的程序员提供的
package org.qiu.myspring.bean;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.qiu.myspring.bean * @date 2022-11-12-11:07 * @since 1.0 */public class User { private String name; private int age; @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; }} package org.qiu.myspring.bean;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.qiu.myspring.bean * @date 2022-11-12-11:08 * @since 1.0 */public class UserDao { public void insert(){ System.out.println("mysql数据库正在保存用户数据......."); }} package org.qiu.myspring.bean;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.qiu.myspring.bean * @date 2022-11-12-11:09 * @since 1.0 */public class UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void save(){ userDao.insert(); }}
三、准备 myspring.xml 配置文件
将来在框架开发完毕之后,这个文件也是要删除的。因为这个配置文件的提供者应该是使用这个框架的程序员
文件名随意,我们这里叫做:myspring.xml
文件放在类路径当中即可,我们这里把文件放到类的根路径下
使用value给简单属性赋值。使用ref给非简单属性赋值。
四、编写 ApplicationContext 接口
ApplicationContext 接口中提供一个 getBean() 方法,通过该方法可以获取 Bean 对象。
注意包名:这个接口就是 myspring 框架中的一员了。
package org.myspringframework.core;/** * Spring 应用上下文接口 * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.myspringframework.core * @date 2022-11-12-11:22 * @since 1.0 */public interface ApplicationContext { /** * 根据 beanName 获取 bean 对象 * @param beanName myspring 配置文件中 bean 标签的 id * @return 对应的单例 bean 对象 */ Object getBean(String beanName);}
五、编写 ClassPathXmlApplicationContext
ClassPathXmlApplicationContext 是 ApplicationContext 接口的实现类。该类从类路径当中加载myspring.xml 配置文件
package org.myspringframework.core;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.myspringframework.core * @date 2022-11-12-11:24 * @since 1.0 */public class ClassPathXmlApplicationContext implements ApplicationContext{ /** * 解析 myspring 配置文件,初始化所有的 bean 对象 * @param configLocation spring 配置文件的路径 * (使用 ClassPathXmlApplicationContext 配置文件应当放到类路径下) */ public ClassPathXmlApplicationContext(String configLocation) { } @Override public Object getBean(String beanName) { return null; }}
六、确定采用 Map 集合存储 Bean
确定采用Map集合存储Bean实例
Map集合的key存储beanId,value存储Bean实例。Map
在ClassPathXmlApplicationContext类中添加Map属性
并且在ClassPathXmlApplicationContext类中添加构造方法,该构造方法的参数接收myspring.xml文件
同时实现getBean方法
package org.myspringframework.core;import java.util.HashMap;import java.util.Map;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.myspringframework.core * @date 2022-11-12-11:24 * @since 1.0 */public class ClassPathXmlApplicationContext implements ApplicationContext{ private Map singletonObjects = new HashMap(); /** * 解析 myspring 配置文件,初始化所有的 bean 对象 * @param configLocation spring 配置文件的路径 * (使用 ClassPathXmlApplicationContext 配置文件应当放到类路径下) */ public ClassPathXmlApplicationContext(String configLocation) { // 解析 myspring.xml 配置文件,实例化 bean,将 bean 存放到 singletonObjects 集合中 } /** * 根据 beanName 在 singletonObjects 集合中获取对应的 bean 对象 * @param beanName myspring 配置文件中 bean 标签的 id * @return 对应的 bean 对象 */ @Override public Object getBean(String beanName) { return singletonObjects.get(beanName); }}
七、解析配置文件实例化所有 Bean
在ClassPathXmlApplicationContext 的构造方法中解析配置文件,获取所有 bean 的类名,通过反射机制调用无参数构造方法创建 Bean。并且将 Bean 对象存放到 Map 集合中
/** * 解析 myspring 配置文件,初始化所有的 bean 对象 * @param configLocation spring 配置文件的路径 * (使用 ClassPathXmlApplicationContext 配置文件应当放到类路径下) */public ClassPathXmlApplicationContext(String configLocation) { try { // 解析 myspring.xml 配置文件,实例化 bean,将 bean 存放到 singletonObjects 集合中 // dom4j 解析 XML 文件的核心对象 SAXReader reader = new SAXReader(); // 获取一个输入流,指向配置文件 InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation); // 读文件 Document document = reader.read(in); // 获取所有的 bean 标签 List nodes = document.selectNodes("//bean"); // 遍历 bean 标签 nodes.forEach(node -> { try { // 向下转型,为了使用 Element 接口中更丰富的方法 Element beanElement = (Element) node; // 获取 id 属性 String id = beanElement.attributeValue("id"); // 获取 class 属性 String className = beanElement.attributeValue("class"); logger.info("bean id:" + id + ",bean className:" + className); // 使用反射机制创建对象,并存储到 Map 集合中,提前曝光 Class clazz = Class.forName(className); Object bean = clazz.newInstance(); singletonObjects.put(id,bean); // 记录日志 logger.info(singletonObjects.toString()); } catch (Exception e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); }}
八、测试能否获取到 Bean
@Testpublic void testMySpring(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml"); Object user = applicationContext.getBean("user"); Object userDao = applicationContext.getBean("userDaoBean"); Object userService = applicationContext.getBean("userService"); System.out.println(user); System.out.println(userDao); System.out.println(userService);} 运行结果:
九、给 Bean 的属性赋值
通过反射机制调用 set 方法,给 Bean 的属性赋值
package org.myspringframework.core;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.Node;import org.dom4j.io.SAXReader;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.InputStream;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.HashMap;import java.util.List;import java.util.Map;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.myspringframework.core * @date 2022-11-12-11:24 * @since 1.0 */public class ClassPathXmlApplicationContext implements ApplicationContext{ private static final Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class); private Map singletonObjects = new HashMap(); /** * 解析 myspring 配置文件,初始化所有的 bean 对象 * @param configLocation spring 配置文件的路径 * (使用 ClassPathXmlApplicationContext 配置文件应当放到类路径下) */ public ClassPathXmlApplicationContext(String configLocation) { try { // 解析 myspring.xml 配置文件,实例化 bean,将 bean 存放到 singletonObjects 集合中 // dom4j 解析 XML 文件的核心对象 SAXReader reader = new SAXReader(); // 获取一个输入流,指向配置文件 InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation); // 读文件 Document document = reader.read(in); // 获取所有的 bean 标签 List nodes = document.selectNodes("//bean"); // 遍历 bean 标签 nodes.forEach(node -> { try { // 向下转型,为了使用 Element 接口中更丰富的方法 Element beanElement = (Element) node; // 获取 id 属性 String id = beanElement.attributeValue("id"); // 获取 class 属性 String className = beanElement.attributeValue("class"); logger.info("bean id:" + id + ",bean className:" + className); // 使用反射机制创建对象,并存储到 Map 集合中,提前曝光 Class clazz = Class.forName(className); Object bean = clazz.newInstance(); singletonObjects.put(id,bean); // 记录日志 logger.info(singletonObjects.toString()); } catch (Exception e) { e.printStackTrace(); } }); nodes.forEach(node -> { try { Element beanElement = (Element) node; // 获取 id 属性 String id = beanElement.attributeValue("id"); // 获取 class 属性 String className = beanElement.attributeValue("class"); // 获取 Class Class clazz = Class.forName(className); // 获取该 bean 标签下的所有 属性标签 property 标签 for (Element property : beanElement.elements("property")) { // 获取属性名 String propertyName = property.attributeValue("name"); // 获取属性类型 Field field = clazz.getDeclaredField(propertyName); logger.info("属性名:" + propertyName); // 获取 set 方法名 String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1); // 获取 set 方法 Method setMethod = clazz.getDeclaredMethod(setMethodName, field.getType()); // 获取具体的值 String value = property.attributeValue("value"); String ref = property.attributeValue("ref"); Object realVal = null; // 值是简单类型 if (value != null) { /** * 声明支持的简单类型 * byte short int long float double boolean char * Byte Short Integer Long Float Double Boolean Character * String */ // 属性类型名 // String propertyTypeSimpleName = field.getType().getName(); 带有包名 String propertyTypeSimpleName = field.getType().getSimpleName(); switch (propertyTypeSimpleName){ case "byte": realVal = Byte.parseByte(value); break; case "short": realVal = Short.parseShort(value); break; case "int": realVal = Integer.parseInt(value); break; case "long": realVal = Long.parseLong(value); break; case "float": realVal = Float.parseFloat(value); break; case "double": realVal = Double.parseDouble(value); break; case "boolean": realVal = Boolean.parseBoolean(value); break; case "char": realVal = value.charAt(0); break; case "Byte": realVal = Byte.valueOf(value); break; case "Short": realVal = Short.valueOf(value); break; case "Integer": realVal = Integer.valueOf(value); break; case "Long": realVal = Long.valueOf(value); break; case "Float": realVal = Float.valueOf(value); break; case "Double": realVal = Double.valueOf(value); break; case "Boolean": realVal = Boolean.valueOf(value); break; case "Character": realVal = Character.valueOf(value.charAt(0)); break; case "String": realVal = value; break; } // 调用 set 方法 setMethod.invoke(singletonObjects.get(id), realVal); } // 值是非简单类型 if (ref != null) { // 调用 set 方法 setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref)); } } } catch (Exception e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } /** * 根据 beanName 在 singletonObjects 集合中获取对应的 bean 对象 * @param beanName myspring 配置文件中 bean 标签的 id * @return 对应的 bean 对象 */ @Override public Object getBean(String beanName) { return singletonObjects.get(beanName); }} @Testpublic void testMySpring(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml"); Object user = applicationContext.getBean("user"); System.out.println(user); UserService userService = (UserService) applicationContext.getBean("userService"); userService.save();} 运行结果:
十、打包发布
将多余的类以及配置文件删除,使用 maven 打包发布
十一、站在程序员角度使用 mySpring 框架
org.myspringframework mySpring 1.0.0 junit junit 4.13.2 test
package org.qiu.myspring.bean;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.qiu.myspring.bean * @date 2022-11-12-14:43 * @since 1.0 */public class Vip { private String name; private int age; private double height; @Override public String toString() { return "Vip{" + "name='" + name + '\'' + ", age=" + age + ", height=" + height + '}'; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public void setHeight(double height) { this.height = height; }} package org.qiu.myspring.bean;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.qiu.myspring.bean * @date 2022-11-12-14:43 * @since 1.0 */public class OrderDao { public void insert(){ System.out.println("正在保存订单信息......"); }} package org.qiu.myspring.bean;/** * @author 秋玄 * @version 1.0 * @email qiu_2022@aliyun.com * @project Spring * @package org.qiu.myspring.bean * @date 2022-11-12-14:44 * @since 1.0 */public class OrderService { private OrderDao orderDao; public void setOrderDao(OrderDao orderDao) { this.orderDao = orderDao; } public void generate(){ orderDao.insert(); }} @Testpublic void testMySpring(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("myspring.xml"); Object vip = applicationContext.getBean("vip"); System.out.println(vip); OrderService orderService = (OrderService) applicationContext.getBean("orderService"); orderService.generate();} 运行结果:
一 叶 知 秋,奥 妙 玄 心
下一篇:ps4怎么psn服务器?