基於Java反射技術實現簡單IOC容器
阿新 • • 發佈:2020-07-08
前言
首先思考一個問題,如果你正在做一個複雜的系統,一個系統模組內有幾百個功能業務類,這些類需要使用同一些物件來進行工作。那麼,你會怎樣去管理這些通用且一樣的物件呢?
學習過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,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}
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。