码迷,mamicode.com
首页 > 编程语言 > 详细

使用Java反射技术写一个简单的IOC容器

时间:2020-07-05 10:29:38      阅读:69      评论:0      收藏:0      [点我收藏+]

标签:tom   创建   att   attribute   实体类   stc   内容   需要   复杂   

前言

首先思考一个问题,如果你正在做一个复杂的系统,一个系统模块内有几百个功能业务类,这些类需要使用同一些对象来进行工作。那么,你会怎样去管理这些通用且一样的
对象呢?

学习过Spring的朋友会知道,Spring框架为此提供了一种非常先进的思想,即IOC(控制反转)。Spring可以理解为一个工厂,负责对象的创建和对象间关系的维护。IoC即控制反转,简单说就是之前需要使用new的方式创建对象,而Spring框架会从XML文件中根据配置的信息来创建对象,然后放进它自己的容器之中。在程序要使用到该对象的时候,自动注入。

下面就来做一个最简单的IOC容器。

  • 1.创建一个实体类,比如学生类,汽车类
  • 2.创建XML文件配置对象的信息
  • 3.编写一个IOC容器类。这个类工作起来,首先加载XML文件,扫描自己配置的对象信息,之后使用反射技术创建对象,最后将这些对象放进自己的Map集合中(容器)。外部想要调用这些对象,那么就使用Map的键,来拿到这个集合中对应的值(对象)。

技术图片

编写一个喜闻乐见的Student学生类。

  • 我做的比较简单,没有使用get() set()方法。
  • 后面使用反射技术可以强制给 private 修饰的属性赋值
package cn.haidnor.bean;

public class Student {
    /** 学生姓名 */
    private String name;
    /** 学生性别 */
    private String gender;
    /** 学生年龄 */
    private int age;
    
    @Override
    public String toString() {
        return "Student{" +
                "name=‘" + name + ‘\‘‘ +
                ", gender=‘" + gender + ‘\‘‘ +
                ", age=" + age +
                ‘}‘;
    }
}

创建XML文件,配置对象信息

  • id 表示在IOC容器(Map)的键
  • class 表示对象类的全类名
  • name 表示对象的各种属性名
  • property下的文本节点表示该属性的值
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="stu1" class="cn.haidnor.bean.Student">
        <property name="name">Lucy</property>
        <property name="age">18</property>
        <property name="gender">female</property>
    </bean>

    <bean id="stu2" class="cn.haidnor.bean.Student">
        <property name="name">Tom</property>
        <property name="age">21</property>
        <property name="gender">male</property>
    </bean>

    <bean id="stu3" class="cn.haidnor.bean.Student">
        <property name="name">LiLi</property>
        <property name="age">23</property>
        <property name="gender">female</property>
    </bean>
</beans>

编写IOC容器类

  • 1.首先根据XML中的配置文件,生成学生对象
  • 2.所有的对象都放入到一个Map中
  • 3.提供一个getBean()的方法,传入配置文件中的id,返回对应的对象
package cn.haidnor.core;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;


public class SpringIOC {

    /**
     * 配置文件地址
     */
    private static final String CONFIGURATION_PATH = "resources/applicationContext.xml";

    /**
     * ioc容器
     */
    private static Map<String, Object> ioc = new HashMap<>();

    static {
        initialization();
    }

    /**
     * 从 ioc 容器中获取指定 bean
     *
     * @param name 需要获取的 bean 的 id, 对应 XML 配置文件中的 bean id
     * @return bean
     */
    public static Object getBean(String name) {
        return ioc.get(name);
    }

    /**
     * 初始化容器
     */
    private static void initialization() {
        Document document = null;

        try {
            DocumentBuilderFactory bdf = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = bdf.newDocumentBuilder();
            document = documentBuilder.parse(CONFIGURATION_PATH);
        } catch (Exception e) {
            e.printStackTrace();
        }

        NodeList beanNodes = document.getElementsByTagName("bean");

        for (int i = 0; i < beanNodes.getLength(); i++) {
            Node node = beanNodes.item(i);
            reloadBean(node);
        }
    }

    /**
     * 装载 benn
     *
     * @param beanNode xml 文件 bean 根节点
     */
    private static void reloadBean(Node beanNode) {
        Element bean = (Element) beanNode;

        String id = bean.getAttribute("id");            // IOC 容器中 bean 的名字
        String beanClass = bean.getAttribute("class");  // 全类名

        // 每个 bean 节点下的全部 property 节点
        NodeList childNodes = beanNode.getChildNodes();
        Map<String, String> attributeMap = reloadAttribute(childNodes);

        // 使用反射构造 bean 对象
        Object instance = creatBean(beanClass, attributeMap);

        // 将所有的 bean 对象放入容器中
        ioc.put(id, instance);
    }

    /**
     * 加载 bean 的属性值
     *
     * @param attributeNodes 所有的属性 property 节点
     * @return Map 属性的名字和值集合
     */
    private static Map<String, String> reloadAttribute(NodeList attributeNodes) {
        Map<String, String> keyValue = new HashMap<>();
        for (int i = 0; i < attributeNodes.getLength(); i++) {
            Node filed = attributeNodes.item(i);
            if (filed.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) filed;
                String fileName = element.getAttribute("name");
                String value = element.getFirstChild().getNodeValue();
                keyValue.put(fileName, value);
            }
        }
        return keyValue;
    }

    /**
     * 构造bean对象
     *
     * @param className  全类名
     * @param attributes 每个对象的属性和
     * @return Object 构造完成的 bean 对象
     */
    private static Object creatBean(String className, Map<String, String> attributes) {
        Object instance = null;
        try {
            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();
            Field[] fields = clazz.getDeclaredFields();

            for (Field field : fields) {
                setFiledValue(instance, field, attributes);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 为实例对象的属性赋值
     *
     * @param instance   实例对象
     * @param field      属性字段对象
     * @param attributes 属性名与属性值的 Map 集合
     */
    private static void setFiledValue(Object instance, Field field, Map<String, String> attributes) {
        // 忽略 field 权限检查
        field.setAccessible(true);

        String type = field.getType().toString();
        String name = field.getName();

        try {
            switch (type) {
                case "char":
                    field.setChar(instance, attributes.get(name).charAt(0));
                    break;

                case "class java.lang.Boolean":
                case "boolean":
                    field.setBoolean(instance, Boolean.parseBoolean(attributes.get(name)));
                    break;

                case "class java.lang.Byte":
                case "byte":
                    field.setByte(instance, Byte.parseByte(attributes.get(name)));
                    break;

                case "class java.lang.Float":
                case "float":
                    field.setFloat(instance, Float.parseFloat(attributes.get(name)));
                    break;

                case "class java.lang.Integer":
                case "int":
                    field.setInt(instance, Integer.parseInt(attributes.get(name)));
                    break;

                case "class java.lang.Long":
                case "long":
                    field.setLong(instance, Long.parseLong(attributes.get(name)));
                    break;

                case "class java.lang.Short":
                case "short":
                    field.setShort(instance, Short.parseShort(attributes.get(name)));
                    break;

                default:
                    field.set(instance, attributes.get(name));
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

最后编写测试类

  • 不使用new的方式创建学生对象
  • 使用ioc容器getBean()方法获取对象
  • 调用对象的复写的toString()方法
package cn.haidnor.test;

import cn.haidnor.bean.Student;
import cn.haidnor.core.SpringIOC;

public class Test {
    public static void main(String[] args) {
        // 不使用 new 的方式创建对象, 从容器中获取
        Student stu1 = (Student) SpringIOC.getBean("stu3");
        // 调用学生类的方法,打印信息
        System.out.println(stu1.toString());
    }
}

运行结果,控制台打印输出的内容

Student{name=‘LiLi‘, gender=‘female‘, age=23}

使用Java反射技术写一个简单的IOC容器

标签:tom   创建   att   attribute   实体类   stc   内容   需要   复杂   

原文地址:https://www.cnblogs.com/Haidnor/p/13237793.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!