1. 程式人生 > 程式設計 >基於Java反射技術實現簡單IOC容器

基於Java反射技術實現簡單IOC容器

前言

首先思考一個問題,如果你正在做一個複雜的系統,一個系統模組內有幾百個功能業務類,這些類需要使用同一些物件來進行工作。那麼,你會怎樣去管理這些通用且一樣的物件呢?

學習過Spring的朋友會知道,Spring框架為此提供了一種非常先進的思想,即IOC(控制反轉)。Spring可以理解為一個工廠,負責物件的建立和物件間關係的維護。IoC即控制反轉,簡單說就是之前需要使用new的方式建立物件,而Spring框架會從XML檔案中根據配置的資訊來建立物件,然後放進它自己的容器之中。在程式要使用到該物件的時候,自動注入。

下面就來做一個最簡單的IOC容器。

1.建立一個實體類,比如學生類,汽車類

2.建立XML檔案配置物件的資訊

3.編寫一個IOC容器類。這個類工作起來,首先載入XML檔案,掃描自己配置的物件資訊,之後使用反射技術建立物件,最後將這些

物件放進自己的Map集合中(容器)。外部想要呼叫這些物件,那麼就使用Map的鍵,來拿到這個集合中對應的值(物件)。

基於Java反射技術實現簡單IOC容器

編寫一個喜聞樂見的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,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}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。