当前位置:网站首页>基于xml实现简单的对象自动创建和依赖注入
基于xml实现简单的对象自动创建和依赖注入
2022-08-08 06:27:00 【写做四月一日的四月一日】
IOC:控制反转,将把对象的创建、复制,管理工作交给业务代码之外的容器来实现。由外部资源来实现对象的创建。
使用ioc技术可以将创建对象的代码和业务代码分开,将资源集中管理,实现资源的可配置和易管理;降低使用资源双方的耦合度。
尝试自己实现一个简单的基于xml的IOC容器。
核心技术:xml解析、反射。
创建dtd约束文件
创建一个syyrjxConstraint.xml对配置文件进行内容约束(文件名任意,xml文件引入时注意文件名相同就行了)
<!ELEMENT beans (bean+) ><!--beans标签下至少要有一个bean标签-->
<!ELEMENT bean (property*)><!--bean标签下可以有任意个property标签-->
<!ELEMENT property (#PCDATA)>
<!ATTLIST bean id ID #REQUIRED><!--bean标签的id属性做为id值不可以重复-->
<!ATTLIST bean className #REQUIRED><!--全限定类名-->
<!ATTLIST property name #REQUIRED><!--属性名-->
<!ATTLIST property value> <!--属性值,基本数据类型-->
<!ATTLIST property reference IDREF> <!--属性值,引用类型-->
创建实体类
学生实体类
package xyz.syyrjx.entity;
public class Student {
private int id;
private String name;
private int age;
private ClassRoom classRoom;
public Student() {}
public Student(int id, String name, int age, ClassRoom classRoom) {
this.id = id;
this.name = name;
this.age = age;
this.classRoom = classRoom;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public ClassRoom getClassRoom() {
return classRoom;
}
public void setClassRoom(ClassRoom classRoom) {
this.classRoom = classRoom;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", classRoom=" + classRoom +
'}';
}
}
教室实体类
package xyz.syyrjx.entity;
public class ClassRoom {
private int grade;
private int classNumber;
public ClassRoom() {
}
public ClassRoom(int grade, int classNumber) {
this.grade = grade;
this.classNumber = classNumber;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public int getClassNumber() {
return classNumber;
}
public void setClassNumber(int classNumber) {
this.classNumber = classNumber;
}
@Override
public String toString() {
return this.grade + "年级" + this.classNumber + "班";
}
}
编写xml文件
xml文件的内容就是我们需要创建的对象的信息,dtd约束文件和xml文件在一个目录下,如果不在一个目录下,需要修改文件路径名称。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans SYSTEM "syyrjxConstraint.dtd">
<beans>
<bean id="student" className="xyz.syyrjx.entity.Student">
<property name="id" value="1" />
<property name="name" value="张三"/>
<property name="age" value="20"/>
<property name="classRoom" reference="classRoom"/>
</bean>
<bean id="classRoom" className="xyz.syyrjx.entity.ClassRoom">
<property name="grade" value="1"/>
<property name="classNumber" value="3"/>
</bean>
</beans>
写一个容器类
package xyz.syyrjx.container;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.util.*;
public class BeanFactory {
private static final Map<String,Object> BEAN_MAP = new HashMap<>();
private Map<String,Object> beanMap;
public BeanFactory(){
beanMap = BEAN_MAP;
}
/**
* 类加载时解析xml文件并创建对象到容器种
*/
static {
//等待区,基本数据类型注入完成后再执行引用类型的注入,在基本数据类型注入阶段将需要注入的引用数据类型存入等待区
Map<String,List<Pair<String,String>>> waitArea = new HashMap<>();
//字符集
String charsetName = "utf-8";
//配置文件名
String configurationFileName = null;
try {
configurationFileName = URLDecoder.decode(Objects.requireNonNull(Class.forName("xyz.syyrjx.container.BeanFactory").getClassLoader().getResource("conf/syyrjxBeans.xml")).getPath(),charsetName);
} catch (ClassNotFoundException | UnsupportedEncodingException e) {
e.printStackTrace();
}
File configurationFile = new File(configurationFileName);
//解析xml文件位dom树
Document document = null;
try {
document = Jsoup.parse(configurationFile,charsetName);
} catch (IOException e) {
e.printStackTrace();
}
if (null != document){
//根标签名称
String rootTag = "beans";
//根标签只有一个
Element beans = document.getElementsByTag(rootTag).get(0);
//获取根标签下的子标签(beans下的bean)
Elements beanList = beans.children();
for (Element bean : beanList) {
//bean标签只有id和className属性
String id = bean.attr("id");
String className = bean.attr("className");
//反射生成对象
Class clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Object obj = null;
try {
obj = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//对象的属性列表
Field[] fields = clazz.getDeclaredFields();
//获取属性标签
Elements properties = bean.children();
for (Element property : properties) {
//获取三个值
String name = property.attr("name");
String value = property.attr("value");
String reference = property.attr("reference");
if ("".equals(value) && "".equals(reference)){
continue;
}
if (!"".equals(reference)){
//加入等待区
if (!waitArea.containsKey(name)){
waitArea.put(id,new ArrayList<>());
}
Pair<String,String> pair = new Pair<>(name,reference);
waitArea.get(id).add(pair);
}else{
//解析,赋值
for (Field field : fields){
field.setAccessible(true);
if (Objects.equals(field.getName(),name)){
String type = field.getType().getSimpleName();
try {
switch (type){
case "int":
case "Integer":
field.set(obj,Integer.parseInt(value));
break;
case "boolean":
case "Boolean":
field.set(obj,Boolean.parseBoolean(value));
break;
case "char":
case "Character":
field.set(obj,value.charAt(0));
break;
case "byte":
case "Byte":
field.set(obj,Byte.parseByte(value));
break;
case "long":
case "Long":
field.set(obj,Long.parseLong(value));
break;
case "double":
case "Double":
field.set(obj,Double.parseDouble(value));
break;
case "float":
case "Float":
field.set(obj,Float.parseFloat(value));
break;
case "short":
case "Short":
field.set(obj,Short.parseShort(value));
break;
default:
field.set(obj,value);
}
}catch (IllegalAccessException e) {
e.printStackTrace();
}
break;
}
}
}
}
//赋值完成加入静态的BEAN_MAP
BEAN_MAP.put(id,obj);
}
//遍历等待区,赋reference值
Set<Map.Entry<String,List<Pair<String,String>>>> entry = waitArea.entrySet();
for (Map.Entry<String, List<Pair<String, String>>> e : entry) {
String id = e.getKey();
List<Pair<String, String>> kAndV = e.getValue();
Object dest = BEAN_MAP.get(id);
Class clazz = dest.getClass();
for (Pair<String,String> pair : kAndV){
String k = pair.getKey();
String v = pair.getVal();
Object src = BEAN_MAP.get(v);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (Objects.equals(k,field.getName())){
field.setAccessible(true);
try {
field.set(dest,src);
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
}
break;
}
}
}
}
}
}
/**
* 获取容器内的对象
* @param id 对象的id值
* @return 返回对象
*/
public Object get(String id){
return beanMap.get(id);
}
/**
* 容器类的内部类,用来封装引用类型属性到等待区
* @param <K> 键类型
* @param <V> 值类型
*/
private static class Pair<K,V>{
K key;
V val;
public Pair() {}
public Pair(K key, V val) {
this.key = key;
this.val = val;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getVal() {
return val;
}
public void setVal(V val) {
this.val = val;
}
}
}
测试能否获取到这个类
欸嘿,获取到了,在我的业务代码中也没有对student对象的创建(new)。容器创建成功。
补充
使用的xml解析工具为jsoup-1.14.3
边栏推荐
猜你喜欢
P17 五子棋的实现4 悔棋功能
[Unity] 自定义日志系统 解决Unity Log的痛点
The state machine control shift register multisim simulation in the process of state variables and state transition conditions don't match
【EagleEye】2020-ECCV-EagleEye: Fast Sub-net Evaluation for Efficient Neural Network Pruning-论文详解
【图形学】12 UnityShader语法入门
Stack queue OJ question sharing and explanation
[总结]Unity Console面板的问题汇总
神经网络的图像识别技术,神经网络的层数怎么看
【图形学】09 UnityShader入门(一)
CSDN21天学习挑战赛之顺序查找
随机推荐
【图形学】14 UnityShader语义(二)
Unity_圆环滑动条(圆形、弧形滑动条)
[Unity] 自定义日志系统 解决Unity Log的痛点
[Unity] GPU动画实现(三)——材质合并
C语言实现冒泡排序及对冒泡排序的优化处理
计算机网络 | 03.[HTTP篇] HTTP缓存技术
tcpdump进行ARP抓包
文件IO实现图片的加密操作
NVIDIA CUDA 高度并行处理器编程(七):并行模式:前缀和
软件工具 | 04.Typora搭配PicGo-Core实现用时间命名图片
霍夫曼树(赫夫曼树、哈夫曼树)
Folder permission configuration for Unity local IIS service construction
firefly rk3399 硬件解码,多通道解码
Unity_预制体批量编辑器
NVIDIA CUDA Highly Parallel Processor Programming (8): Parallel Mode: Histogram Calculation
[Unity] C#使用委托事件与字典实现unity消息中心(观察者模式)
【Unity】unity中对象池的使用
线程P01——进程 并发 并行 线程的使用
如何使用conda,pip安装、更新、查看和卸载重装Pytorch?
【图形学】03 数学部分(三、各种变换矩阵)