# myspring **Repository Path**: mr_sen/myspring ## Basic Information - **Project Name**: myspring - **Description**: 自定义ioc框架 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-09-20 - **Last Updated**: 2021-09-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 自定义ioc容器 ## 步骤流程 ### 1. 要定义一个ioc的配置文件 ```xml ``` ### 2.创建BeanFactory类,来解析xml文件并创建对象保存到ioc中,同时把暴露ioc对象访问接口 ```java package com.lagou.factory; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author 张在森 * @create 22:54 *

* 工厂类,用于生产(通过反射的方式)对象 *

* 完成的工作 * 1. 读取并解析xml文件,同时用反射方式创建对象,并把对象放到map集合中 * 2. 对外提供获取对象实例的接口 */ public class BeanFactory { private static Map beans = new HashMap<>(); static { // step1 :读取并解析xml,并将创建的对象放到map中年 InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"); SAXReader saxReader = new SAXReader(); try { System.out.println("ioc 被实例化....."); /* * */ Document document = saxReader.read(resourceAsStream); Element rootElement = document.getRootElement(); /* * xpath :使用路径表达式来选取xml文档中的节点 * 常用路径表达式: * nodename: 选取此节点的所有子节点 * / : 从跟地点选取 * //: 从当前节点下选择节点,不考虑他们的位置 * . : 选取当前节点 * .. : 选取当前节点的父节点 * @ : 选取属性 * */ List list = rootElement.selectNodes("//bean"); //region == 使用for循环进行遍历 // for (Element bean : list) { // String aClass = bean.attributeValue("class"); // String id = bean.attributeValue("id"); // Class clazz = Class.forName(aClass); // Object o = clazz.newInstance(); // beans.put(id, o); // // // beans.put(id, o); // } // endregion list.forEach(bean -> { String aClass = bean.attributeValue("class"); String id = bean.attributeValue("id"); try { Class clazz = Class.forName(aClass); Object o = clazz.newInstance(); beans.put(id, o); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } // step2:暴露访问对象 public static Object getBean(String id) { return beans.get(id); } public static Object getBean(Class type) { for (Map.Entry entry : beans.entrySet()) { String id = entry.getKey(); Object obj = entry.getValue(); Class aClass = obj.getClass(); // aClass.getTypeName() if (obj.getClass().getTypeName().equals(type.getTypeName())) { return obj; } } throw new RuntimeException("未查到指定类型的bean"); } } ``` ## 题外话 dom4j 和xpath的使用 - 依赖 ```xml dom4j dom4j 1.6.1 jaxen jaxen 1.1.6 ``` - xpath的使用 xpath :使用路径表达式来选取xml文档中的节点 常用路径表达式: nodename: 选取此节点的所有子节点 / : 从跟地点选取 //: 从当前节点下选择节点,不考虑他们的位置 . : 选取当前节点 .. : 选取当前节点的父节点 @ : 选取属性 ## 待解决 使用foreach 等方法, lamda表达式中如何使用当前方法中的局部变量 ## 升级,service层中依赖dao层的对象使用setter方法注入 修改BeanFactory中初始化方法 ```java package com.lagou.factory; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author 张在森 * @create 22:54 *

* 工厂类,用于生产(通过反射的方式)对象 *

* 完成的工作 * 1. 读取并解析xml文件,同时用反射方式创建对象,并把对象放到map集合中 * 2. 对外提供获取对象实例的接口 */ public class BeanFactory { private static Map beans = new HashMap<>(); static { // step1 :读取并解析xml,并将创建的对象放到map中年 InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"); SAXReader saxReader = new SAXReader(); try { System.out.println("ioc 被实例化....."); /* * */ Document document = saxReader.read(resourceAsStream); Element rootElement = document.getRootElement(); /* * xpath :使用路径表达式来选取xml文档中的节点 * 常用路径表达式: * nodename: 选取此节点的所有子节点 * / : 从跟地点选取 * //: 从当前节点下选择节点,不考虑他们的位置 * . : 选取当前节点 * .. : 选取当前节点的父节点 * @ : 选取属性 * */ List list = rootElement.selectNodes("//bean"); //region == 使用for循环进行遍历 // for (Element bean : list) { // String aClass = bean.attributeValue("class"); // String id = bean.attributeValue("id"); // Class clazz = Class.forName(aClass); // Object o = clazz.newInstance(); // beans.put(id, o); // // // beans.put(id, o); // } // endregion list.forEach(bean -> { String aClass = bean.attributeValue("class"); String id = bean.attributeValue("id"); try { Class clazz = Class.forName(aClass); Object o = clazz.newInstance(); beans.put(id, o); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }); // 实例化完成后维护对象的依赖关系,检测那些对象需要传值 List properties = rootElement.selectNodes("//property"); properties.forEach(property -> { System.out.println("bean 依赖开始装配....."); /* */ try { // 依赖的信息 String name = property.attributeValue("name"); String ref = property.attributeValue("ref"); // Class properyClass = Class.forName(ref); Object propertyObj = getBean(ref); // 父对象的信息 Element parent = property.getParent(); String id = parent.attributeValue("id"); Object o = beans.get(id); Class aClass = o.getClass(); // 这里会不会有bug,setter方法会不会有重载的形式,还有会不不会一个bean先有多个property标签 Method method = aClass.getMethod("set" + name, propertyObj.getClass().getInterfaces()[0]); method.invoke(o, propertyObj); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }); } catch (Exception e) { e.printStackTrace(); } } // step2:暴露访问对象 // 使用id获取对象 public static Object getBean(String id) { return beans.get(id); } // 使用bean的类型获取对象 public static Object getBean(Class type) { for (Map.Entry entry : beans.entrySet()) { String id = entry.getKey(); Object obj = entry.getValue(); Class aClass = obj.getClass(); // aClass.getTypeName() if (obj.getClass().getTypeName().equals(type.getTypeName())) { return obj; } } throw new RuntimeException("未查到指定类型的bean"); } } ``` ### 存在的疑问或者说bug setter方法会不会重载,这样用setter注入就会有问题 ## 事务管理 如何让service中的方法有事务。 1. 首先让执行service层 的方法中的connection对象是同一个,这样就可以进行事务管理 - 解决方案: 把connection对象绑到线程上,ThreadLocal上 2. Dao层的connection链接从ThreadLocal中获取,同时关闭自动提交、 ### v2升级 不再把事务代码耦合到service层的方法中 解决方案:使用动态代理 - 新增一个TransactionManger类,用于事务管理 - 新增ProxyFactory类,用于动态代理 依赖关系 - TransactionManager类依赖ConnectionUtil来获取链接 - ProxyFactory类依赖TransactionManager来进行事务控制