基于Java反射技術(shù)實(shí)現(xiàn)簡(jiǎn)單IOC容器
前言
首先思考一個(gè)問題,如果你正在做一個(gè)復(fù)雜的系統(tǒng),一個(gè)系統(tǒng)模塊內(nèi)有幾百個(gè)功能業(yè)務(wù)類,這些類需要使用同一些對(duì)象來進(jìn)行工作。那么,你會(huì)怎樣去管理這些通用且一樣的對(duì)象呢?
學(xué)習(xí)過Spring的朋友會(huì)知道,Spring框架為此提供了一種非常先進(jìn)的思想,即IOC(控制反轉(zhuǎn))。Spring可以理解為一個(gè)工廠,負(fù)責(zé)對(duì)象的創(chuàng)建和對(duì)象間關(guān)系的維護(hù)。IoC即控制反轉(zhuǎn),簡(jiǎn)單說就是之前需要使用new的方式創(chuàng)建對(duì)象,而Spring框架會(huì)從XML文件中根據(jù)配置的信息來創(chuàng)建對(duì)象,然后放進(jìn)它自己的容器之中。在程序要使用到該對(duì)象的時(shí)候,自動(dòng)注入。
下面就來做一個(gè)最簡(jiǎn)單的IOC容器。
1.創(chuàng)建一個(gè)實(shí)體類,比如學(xué)生類,汽車類
2.創(chuàng)建XML文件配置對(duì)象的信息
3.編寫一個(gè)IOC容器類。這個(gè)類工作起來,首先加載XML文件,掃描自己配置的對(duì)象信息,之后使用反射技術(shù)創(chuàng)建對(duì)象,最后將這些
對(duì)象放進(jìn)自己的Map集合中(容器)。外部想要調(diào)用這些對(duì)象,那么就使用Map的鍵,來拿到這個(gè)集合中對(duì)應(yīng)的值(對(duì)象)。

編寫一個(gè)喜聞樂見的Student學(xué)生類。
我做的比較簡(jiǎn)單,沒有使用get() set()方法。
后面使用反射技術(shù)可以強(qiáng)制給 private 修飾的屬性賦值
package cn.haidnor.bean;
public class Student {
/** 學(xué)生姓名 */
private String name;
/** 學(xué)生性別 */
private String gender;
/** 學(xué)生年齡 */
private int age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
}
創(chuàng)建XML文件,配置對(duì)象信息
- id 表示在IOC容器(Map)的鍵
- class 表示對(duì)象類的全類名
- name 表示對(duì)象的各種屬性名
- property下的文本節(jié)點(diǎn)表示該屬性的值
<?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.首先根據(jù)XML中的配置文件,生成學(xué)生對(duì)象
2.所有的對(duì)象都放入到一個(gè)Map中
3.提供一個(gè)getBean()的方法,傳入配置文件中的id,返回對(duì)應(yīng)的對(duì)象
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, 對(duì)應(yīng) 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 根節(jié)點(diǎn)
*/
private static void reloadBean(Node beanNode) {
Element bean = (Element) beanNode;
String id = bean.getAttribute("id"); // IOC 容器中 bean 的名字
String beanClass = bean.getAttribute("class"); // 全類名
// 每個(gè) bean 節(jié)點(diǎn)下的全部 property 節(jié)點(diǎn)
NodeList childNodes = beanNode.getChildNodes();
Map<String, String> attributeMap = reloadAttribute(childNodes);
// 使用反射構(gòu)造 bean 對(duì)象
Object instance = creatBean(beanClass, attributeMap);
// 將所有的 bean 對(duì)象放入容器中
ioc.put(id, instance);
}
/**
* 加載 bean 的屬性值
*
* @param attributeNodes 所有的屬性 property 節(jié)點(diǎn)
* @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;
}
/**
* 構(gòu)造bean對(duì)象
*
* @param className 全類名
* @param attributes 每個(gè)對(duì)象的屬性和
* @return Object 構(gòu)造完成的 bean 對(duì)象
*/
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;
}
/**
* 為實(shí)例對(duì)象的屬性賦值
*
* @param instance 實(shí)例對(duì)象
* @param field 屬性字段對(duì)象
* @param attributes 屬性名與屬性值的 Map 集合
*/
private static void setFiledValue(Object instance, Field field, Map<String, String> attributes) {
// 忽略 field 權(quán)限檢查
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();
}
}
}
最后編寫測(cè)試類
- 不使用new的方式創(chuàng)建學(xué)生對(duì)象
- 使用ioc容器getBean()方法獲取對(duì)象
- 調(diào)用對(duì)象的復(fù)寫的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 的方式創(chuàng)建對(duì)象, 從容器中獲取
Student stu1 = (Student) SpringIOC.getBean("stu3");
// 調(diào)用學(xué)生類的方法,打印信息
System.out.println(stu1.toString());
}
}
運(yùn)行結(jié)果,控制臺(tái)打印輸出的內(nèi)容
Student{name='LiLi', gender='female', age=23}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java設(shè)計(jì)模式學(xué)習(xí)之策略模式
這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式學(xué)習(xí)之策略模式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
idea如何修改文件的file is read-only問題
這篇文章主要介紹了idea如何修改文件的file is read-only問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
解釋為什么Java中“1000==1000”為false而”100==100“為true
在日常編程中,我們經(jīng)常遇到一些看似簡(jiǎn)單卻隱藏著復(fù)雜邏輯的問題,這篇文章主要介紹了解釋為什么Java中“1000==1000”為false而”100==100“為true,需要的朋友可以參考下2024-01-01
SpringBoot后端數(shù)據(jù)校驗(yàn)實(shí)戰(zhàn)操作指南
在項(xiàng)?開發(fā)中,對(duì)于前端提交的表單,后臺(tái)接?接收到表單數(shù)據(jù)后,為了保證程序的嚴(yán)謹(jǐn)性,通常后端會(huì)加?業(yè)務(wù)參數(shù)的合法校驗(yàn)操作來避免程序的?技術(shù)性?bug,這篇文章主要給大家介紹了關(guān)于SpringBoot后端數(shù)據(jù)校驗(yàn)的相關(guān)資料,需要的朋友可以參考下2022-07-07
SpringBoot?自定義starter?yaml提示失效問題及解決方法
在自定義starter后,必不可少會(huì)有properties配置參數(shù)需要指定,而在有時(shí)又不知道為什么出現(xiàn)這個(gè)問題,這篇文章主要介紹了SpringBoot?自定義starter?yaml提示失效問題,需要的朋友可以參考下2022-12-12
java多線程累加計(jì)數(shù)的實(shí)現(xiàn)方法
在多線程協(xié)作任務(wù)中,如何計(jì)算也是很重的,這篇文章主要介紹了java多線程累加計(jì)數(shù)的實(shí)現(xiàn)方法,感興趣的朋友可以了解一下2021-05-05

