Silentdoer

导航

spring的IOC/DI功能实践

一、写在前面:

  做这个Demo主要是为了能够更好的理解Spring的原理,看再多的文章,听再多的讲解最终都最好自己去实现一遍,可以将Spring的功能分块实现,最终自然比较容易将各个功能组合起来。

  这个Demo里主要实现的是Spring的IOC功能,即原本需要通过写硬代码来实现初始化复杂类,现在通过配置就能动态的构建复杂类,复杂类的属性值也由配置动态指定,这种实现方式称之为控制反转IOC或叫依赖注入DI;在本例中主要是实现了Spring早期的IOC功能,即通过xml文件配置beans,然后通过XmlBeanFactory来getBean,这里不涉及太多设计模式、异常处理和性能优化,主要还是为了打通过程。对于Spring的注解形式的IOC功能以及AOP功能等还未实践,有时间了也需要归纳知识去实现一遍方能完全理解和记忆。

二、实现功能:

  实现了Spring的通过配置文件动态的配置bean,并提供scope、lazy-init、constructor-arg、property的属性或子结点的配置;最终通过factory.getBean("id")来获取从配置文件里构建的bean对象。

三、代码及配置:

1.beans.xml的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="stud1" class="me.silentdoer.simulatespring.pojo.Student"/>
    <bean id="str1" class="java.lang.String" scope="singleton" lazy-init="true">
        <constructor-arg value="UUUUUUUU" type="java.lang.String"/>
    </bean>
    <bean id="stud2" class="me.silentdoer.simulatespring.pojo.Student">
        <constructor-arg name="uid" value="500" type="java.lang.Long"/>
        <constructor-arg name="gender" ref="str1" type="java.lang.String"/>
        <property name="name" value="Hello" type="java.lang.String"/>
    </bean>
</beans>

2.pojo类:Student

package me.silentdoer.simulatespring.pojo;

import java.io.Serializable;

public class Student implements Serializable {
    private Long uid;
    private String name;
    private String gender;

    public Student(){
        this.name = "silentdoer";
    }

    public Student(Long uid){
        this.uid = uid;
    }

    public Student(Long uid, String gender){
        this.uid = uid;
        this.gender = gender;
    }

    @Override
    public String toString(){
        return String.format("Student-[uid=%s, name=%s, gender=%s]", this.uid, this.name, this.gender);
    }

    public Long getUid() {
        return uid;
    }

    public void setUid(Long uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

3.基础类型和其包装类的转换类,如value="500"将500转换为Long型

package me.silentdoer.simulatespring.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Author Silentdoer
 * @Since 1.0
 * @Version 1.0
 * @Date 2018-2-23 16:39
 */
public class PrimitiveParser {
    public static  <T> T parse(String type, Object origin){
        Logger logger = LoggerFactory.getLogger("myLogger");
        if(logger.isDebugEnabled()){
            logger.debug(String.format("%s, %s", type, origin));
        }
        Object result = null;
        switch(type){
            case "long":
            case "java.lang.Long":
                result = Long.parseLong(origin.toString());
                break;
            // etc.
            default:
                throw new UnsupportedOperationException("暂不支持");
        }
        return (T) result;
    }
}

4.配置文件bean的包装类

package me.silentdoer.simulatespring.beans.factory.config;

import java.io.Serializable;
import java.util.List;

/**
 * 这个不是VO,也不是涉及RPC的POJO,故可以用基础类型以及有默认值
 * @Author Silentdoer
 * @Since 1.0
 * @Version 1.0
 * @Date 2018-2-19 21:37
 */
public class BeanInfo implements Serializable {
    public static final int SCOPE_PROTOTYPE = 1, SCOPE_SINGLETON = 2;
    private String id;
    private String clazz;
    private Object instance;
    private int scope = SCOPE_SINGLETON;
    private boolean lazyInit = false;
    private List<KeyValueTypePair> constructorArgs;
    private List<KeyValueTypePair> properties;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClazz() {
        return clazz;
    }

    public void setClazz(String clazz) {
        this.clazz = clazz;
    }

    public Object getInstance() {
        return instance;
    }

    public void setInstance(Object instance) {
        this.instance = instance;
    }

    public int getScope() {
        return scope;
    }

    public void setScope(int scope) {
        this.scope = scope;
    }

    public boolean isLazyInit() {
        return lazyInit;
    }

    public void setLazyInit(boolean lazyInit) {
        this.lazyInit = lazyInit;
    }

    public List<KeyValueTypePair> getConstructorArgs() {
        return constructorArgs;
    }

    public void setConstructorArgs(List<KeyValueTypePair> constructorArgs) {
        this.constructorArgs = constructorArgs;
    }

    public List<KeyValueTypePair> getProperties() {
        return properties;
    }

    public void setProperties(List<KeyValueTypePair> properties) {
        this.properties = properties;
    }

    public static class KeyValueTypePair {
        private String key;
        private Object value;
        private String type;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }
}

5.XmlBeanFactory类,用于提供bean

package me.silentdoer.simulatespring.beans.factory;

import me.silentdoer.simulatespring.beans.factory.config.BeanInfo;
import me.silentdoer.simulatespring.util.PrimitiveParser;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

import static me.silentdoer.simulatespring.beans.factory.config.BeanInfo.KeyValueTypePair;

/**
 * @Author Silentdoer
 * @Since 1.0
 * @Version 1.0
 * @Date 2018-2-19 21:01
 */
public class XmlBeanFactory {
    private InputStream resource = null;
    private boolean inited = false;
    private Map<String, BeanInfo> beansInfo;
    private Map<String, Class<?>> primitiveAndWrapperTable;

    public XmlBeanFactory(InputStream inputStream){
        this.resource = inputStream;
        primitiveAndWrapperTable = new HashMap<>(16);
        primitiveAndWrapperTable.put("long", long.class);
        primitiveAndWrapperTable.put("java.lang.Long", Long.class);
        primitiveAndWrapperTable.put("int", int.class);
        primitiveAndWrapperTable.put("java.lang.Integer", Integer.class);
        // etc.
    }

    protected final void init() throws DocumentException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        if (inited) {
            return;
        }
        Logger logger = LoggerFactory.getLogger("myLogger");
        final InputStream config = this.resource;
        if (null == config) {
            throw new IllegalStateException("初始化失败");
        }

        SAXReader reader = new SAXReader();
        Document document = reader.read(config);
        Element root = document.getRootElement();
        List<Element> beans = root.elements("bean");
        final Map<String, BeanInfo> beanInfoMap = this.beansInfo = new LinkedHashMap<String, BeanInfo>(beans.size());
        /** 先构建标签的属性 */
        for (Element bean : beans) {
            Attribute id = bean.attribute("id");
            Attribute clazz = bean.attribute("class");
            Attribute scope = bean.attribute("scope");
            Attribute lazyInit = bean.attribute("lazy-init");
            if (id == null || clazz == null) {
                throw new RuntimeException("配置不合法");
            }
            BeanInfo beanInfo = new BeanInfo();
            beanInfo.setId(id.getValue());
            beanInfo.setClazz(clazz.getValue());
            if (scope != null && scope.getValue().equals("prototype")) {
                beanInfo.setScope(BeanInfo.SCOPE_PROTOTYPE);
            }
            if (lazyInit != null && lazyInit.getValue().equals("true")) {
                beanInfo.setLazyInit(true);
            }
            beanInfoMap.put(id.getValue(), beanInfo);
        }

        /** 构建标签的子结点 */
        for (Element bean : beans) {
            List<Element> constructorParams = bean.elements("constructor-arg");
            List<Element> properties = bean.elements("property");
            String id = bean.attributeValue("id");
            BeanInfo beanInfo = beanInfoMap.get(id);
            List<KeyValueTypePair> conArgs = new ArrayList<KeyValueTypePair>(constructorParams.size());
            beanInfo.setConstructorArgs(conArgs);
            initKeyValueTypePair(conArgs, constructorParams.iterator(), beanInfoMap);

            List<KeyValueTypePair> pros = new ArrayList<KeyValueTypePair>(properties.size());
            beanInfo.setProperties(pros);
            initKeyValueTypePair(pros, properties.iterator(), beanInfoMap);
        }

        /** 根据上面构建出的配置和参数构建bean */
        for(BeanInfo bean : beanInfoMap.values()){
            //boolean prototype = bean.getScope() == BeanInfo.SCOPE_PROTOTYPE;
            boolean lazyInit = bean.isLazyInit();
            // 只要是非lazyInit则初始化后BeanInfo内的instance一定不为null,这个主要是为了先初始化ref的bean后防止重复初始化该bean
            if(!lazyInit && bean.getInstance() == null){
                Object instance = instantiateBean(bean);
                bean.setInstance(instance);
            }
        }
        inited = true;
    }

    /**
     * 通过构建好的BeanInfo初始化具体的实例
     * @param beanInfo
     * @return 实例对象
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     */
    protected Object instantiateBean(BeanInfo beanInfo) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Logger logger = LoggerFactory.getLogger("myLogger");
        Object result = beanInfo.getInstance();
        if(result != null)
            return result;
        Class<?> instanceClazz = Class.forName(beanInfo.getClazz());
        /** ----------------------constructor-arg---------------------- */
        List<KeyValueTypePair> constructorArgs = beanInfo.getConstructorArgs();
        List<Class> conArgTypes = new ArrayList<Class>(16);
        List<Object> conArgs = new ArrayList<Object>(16);
        for(KeyValueTypePair pair : constructorArgs){
            //logger.debug(pair.getType());
            conArgTypes.add(Class.forName(pair.getType()));
            Object value = pair.getValue();
            // ref的情况则先初始化ref对应的bean
            if(BeanInfo.class.isInstance(value)){
                // 递归优先初始化所有的依赖bean
                value = instantiateBean((BeanInfo)value);
            }
            conArgs.add(value);
        }
        /*if(logger.isDebugEnabled()) {
            logger.debug(conArgTypes.toString());
        }*/
        Constructor constructor = instanceClazz.getConstructor(conArgTypes.toArray(new Class[conArgTypes.size()]));
        Object[] initargs = conArgs.toArray(new Object[conArgs.size()]);
        if(logger.isDebugEnabled()){
            for(int i=0;i<initargs.length;i++){
                logger.debug("Tag:" + initargs[i].getClass());
            }
        }
        result = constructor.newInstance(initargs);
        /** ----------------------property---------------------- */
        List<KeyValueTypePair> propertyArgs = beanInfo.getProperties();
        for(KeyValueTypePair pair : propertyArgs){
            String type = pair.getType();
            String name = pair.getKey();
            String setter = String.format("set%s%s", name.substring(0, 1).toUpperCase(), name.substring(1));
            Method setterM = instanceClazz.getMethod(setter, Class.forName(type));
            Object value = pair.getValue();
            if(BeanInfo.class.isInstance(value)){
                value = instantiateBean((BeanInfo) value);
            }
            setterM.invoke(result, value);
        }
        return result;
    }

    /**
     * 通过bean的constructor-arg或property配置填充keyValueTypePairs
     * @param keyValueTypePairs
     * @param iterator
     * @param beansContainer
     */
    protected final void initKeyValueTypePair(final List<KeyValueTypePair> keyValueTypePairs, final Iterator<Element> iterator, final Map<String, BeanInfo> beansContainer){
        Logger logger = LoggerFactory.getLogger("myLogger");
        while(iterator.hasNext()){
            Element next = iterator.next();
            String name = next.attributeValue("name");
            Object value = next.attributeValue("value");
            String ref = next.attributeValue("ref");
            String type = next.attributeValue("type");
            if(value == null && ref == null || value != null && ref != null){
                throw new RuntimeException("配置不合法");
            }
            KeyValueTypePair e = new KeyValueTypePair();
            e.setKey(name);
            e.setType(type);
            if(value != null){
                // 需要转换
                if(primitiveAndWrapperTable.get(type) != null){
                    value = PrimitiveParser.parse(type, value);
                }
                e.setValue(value);
            }else{  // ref
                // NOTE 目前只是初始化BeanInfo,还没到初始化具体的Bean对象
                BeanInfo refBean = beansContainer.get(ref);  // name=gender ref=str1
                // 暂且规定ref的bean要先配置
                if(refBean == null){  // 也可以改成从配置里查找name,有则先初始化该BeanInfo,然后赋值
                    throw new RuntimeException("配置不合法");
                }
                e.setValue(refBean);
            }
            keyValueTypePairs.add(e);
        }
    }

    public <T> T getBean(String id){
        try {
            init();
        }catch (Throwable ex){
            throw new IllegalStateException(ex);
        }
        Object result = null;
        final Map<String, BeanInfo> beans = this.beansInfo;
        BeanInfo beanInfo = beans.get(id);
        result = beanInfo.getInstance();
        if(result == null){
            try {
                result = instantiateBean(beanInfo);
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        if(beanInfo.getScope() == BeanInfo.SCOPE_PROTOTYPE){
            try {
                Method clone = Object.class.getMethod("clone");
                clone.setAccessible(true);
                result = clone.invoke(beanInfo.getInstance());
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        return (T) result;
    }
}

6.main方法类

import me.silentdoer.simulatespring.beans.factory.XmlBeanFactory;
import me.silentdoer.simulatespring.pojo.Student;

import java.io.InputStream;

/**
 * @Author Silentdoer
 * @Since 1.0
 * @Version 1.0
 * @Date 2018-2-19 20:01
 */
public class Entrance {
    public static void main(String[] args){
        InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream("beans.xml");
        System.out.println(resource == null);
        XmlBeanFactory factory = new XmlBeanFactory(resource);
        String str1 = factory.getBean("str1");
        System.out.println(str1);
        Student student = factory.getBean("stud1");
        System.out.println(student);
        Student student2 = factory.getBean("stud2");
        System.out.println(student2);
    }
}

最终main方法输出为:

UUUUUUUU
Student-[uid=null, name=silentdoer, gender=null]
Student-[uid=500, name=Hello, gender=UUUUUUUU]

Spring的基础的IOC功能实现完毕,源码放在我的GitHub上:https://github.com/Silentdoer/demos/tree/master/模拟Spring的IOC功能/Demo.SimulateSpringIOC

posted on 2018-02-21 11:50  Silentdoer  阅读(190)  评论(0编辑  收藏  举报