基于xml实现简单的对象自动创建和依赖注入

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

jsoup下载地址icon-default.png?t=M3K6https://jsoup.org/download

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值