马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
spring
媒介
本章我们模仿实现springIOC的功能,以此加深对IOC实现原理的明白。
Spring IoC容器的实现原理:工厂模式 + 分析XML + 反射机制。
第一步:创建模块myspring
就是创建一个新的项目,不外创建过程不多说,值得一提的是,我们须要导入以下依赖:
上面的两个包可以让我们可以或许利用xpath格式读取xml文件设置。
第二步:预备好我们要管理的Bean
这里利用User、UserDao、UserService三个类来模仿三层架构,也是我们须要管理的bean。
User
UserDao
UserService
第三步:预备myspring.xml设置文件
我们须要预备一个xml框架设置文件,在内里设置好各个bean的注入关系等。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans>
- <bean id="userBean" class="org.example.powernode.myspring.bean.User">
- <property name="name" value="张三"/>
- <property name="age" value="20"/>
- </bean>
- <bean id="userService" class="org.example.myspring.bean.UserService">
- <property name="userDao" ref="userDaoBean"/>
- </bean>
-
- <bean id="userDaoBean" class="org.example.myspring.bean.UserDao"/>
- </beans>
复制代码 第四步:编写ApplicationContext接口
这里编写ApplicationContext接口,原来的spring框架的ApplicationContext接口中getBean()方法是源自BeanFactory()接口,但这里省略写步调,直接将该方法界说在ApplicationContext接口中。
- package org.example.powernode.myspring.framework;
- public interface ApplicationContext {
- Object getBean(String beanName);
- }
复制代码 第五步:编写ClassPathXmlApplicationContext
该类是ApplicationContext接口的实现类,也会实现getBean()方法,是最告急的一步,我们会在构造方法中读取xml中的设置信息,天生对应的bean存放到容器中。然后让getBean()方法可以或许乐成取出实例化完成的bean。
这里就不完备的模仿spring中的三层缓存了,只用一个HashMap聚集即可。
这里我们重要完成以下几步:
- 确定接纳Map聚集存储Bean
- 分析设置文件实例化全部Bean,并放进聚会合
- 给Bean的属性赋值
- package org.example.powernode.myspring.framework;
- import org.dom4j.Document;
- import org.dom4j.Element;
- import org.dom4j.Node;
- import org.dom4j.io.SAXReader;
- import java.io.InputStream;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.util.HashMap;
- import java.util.List;
- public class ClassPathXmlApplicationContext implements ApplicationContext{
- // 创建map集合存储实例化的对象
- private HashMap<String, Object> singletonObjects = new HashMap<>();
- @Override
- public Object getBean(String beanName) {
- // 从map集合中取出实例化的对象
- return singletonObjects.get(beanName);
- }
- // 解析参数传入的配置文件,然后初始化所有bean对象
- public ClassPathXmlApplicationContext(String configLocation) {
- try {
- // 引入dom4j读取器
- SAXReader reader = new SAXReader();
- // 根据传入的配置文件类路径读取配置
- InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
- Document document = reader.read(resourceAsStream);
- // 使用xpath表达式获取所有的bean对象
- List<Node> nodes = document.selectNodes("//bean");
- // 遍历每个bean标签,完成对象实例化操作
- nodes.forEach(node -> {
- try {
- // 从node类型转换为更具体的Element类型,可用方法更多
- Element beanElt = (Element) node;
- // 读取bean标签中的id和class属性值
- String beanName = beanElt.attributeValue("id");
- String className = beanElt.attributeValue("class");
- // 通过从class属性中读取到的类路径创建Class对象
- Class<?> aClass = Class.forName(className);
- // 获取构造器创建实例
- Object o = aClass.getDeclaredConstructor().newInstance();
- // 把创建好的实例存储进map集合,提前"曝光"
- singletonObjects.put(beanName,o);
- }catch (Exception e) {
- e.printStackTrace();
- }
- });
- // 遍历每个bean标签,获取所有的set方法
- nodes.forEach(node -> {
- try {
- Element beanElt = (Element) node;
- // 获取bean标签id
- String beanName = beanElt.attributeValue("id");
- // 获取bean标签类名
- String className = beanElt.attributeValue("class");
- // 通过类名创建Class对象
- Class<?> aClass = Class.forName(className);
- // 获取bean标签中的所有property标签
- List<Element> propertys = beanElt.elements("property");
- // 遍历property标签
- propertys.forEach(property -> {
- try {
- // 获取name属性值,也是我们bean的属性名
- String propertyName = property.attributeValue("name");
- // 通过属性名利用反射机制获取属性对象
- Field field = aClass.getDeclaredField(propertyName);
- // 拼接得到我们要获取的set方法名
- String setName="set"+propertyName.substring(0,1).toUpperCase()+propertyName.substring(1);
- // 通过反射传入方法名和参数类型获取set方法对象
- Method setMethod = aClass.getDeclaredMethod(setName, field.getType());
- // 获取要设置的set参数值
- String propertyValue = property.attributeValue("value");
- String ref = property.attributeValue("ref");
- Object propertyVal = null;
- if (propertyValue!=null){
- // 简单类型注入,需要判断该简单类型具体的类型来进行把value字符串进行类型转换
- // 支持的简单类型有
- // byte short int long float double boolean char
- // Byte Short Integer Long Float Double Boolean Character
- // String
- // 获取属性类型名
- String propertyTypeSimpleName = field.getType().getSimpleName();
- // 根据属性类型名把参数转换为对应的类型
- switch (propertyTypeSimpleName){
- case "byte": case "Byte":
- propertyVal = Byte.valueOf(propertyValue);
- break;
- case "short": case "Short":
- propertyVal = Short.valueOf(propertyValue);
- break;
- case "int": case "Integer":
- propertyVal = Integer.valueOf(propertyValue);
- break;
- case "long": case "Long":
- propertyVal = Long.valueOf(propertyValue);
- break;
- case "float": case "Float":
- propertyVal = Float.valueOf(propertyValue);
- break;
- case "double": case "Double":
- propertyVal = Double.valueOf(propertyValue);
- break;
- case "boolean": case "Boolean":
- propertyVal = Boolean.valueOf(propertyValue);
- break;
- case "char": case "Character":
- propertyVal = propertyValue.charAt(0);
- break;
- case "String":
- propertyVal = propertyValue;
- break;
- }
- // 使用转换完成的参数调用set方法完成属性注入
- setMethod.invoke(singletonObjects.get(beanName),propertyVal);
- }
- if (ref!=null){
- // 非简单类型注入,从map集合中取出具体的实例对象注入即可
- setMethod.invoke(singletonObjects.get(beanName),singletonObjects.get(ref));
- }
- }catch (Exception e) {
- e.printStackTrace();
- }
- });
- } catch (Exception e) {
- e.printStackTrace();
- }
- });
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
复制代码 第六步:打包发布
完成后可以创建一个测试类来调用getBean()查察功能是否正常,确认无误可以利用maven打包成jar包,就完成了模仿spring框架编写的全部操纵。
总结
本章我们模仿实现springIOC的功能,以此加深对springIOC的明白。
|