• 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

  • 相关阅读:
    MIT6.S081 Lab traps
    MIT6.S081 Preparation: Read chapter 7
    MIT6.S081 Lab cow
    MIT6.S081 Lab Multithreading
    MIT6.S081 Preparation: Read chapter 5
    MIT6.S081 Lab networking
    java接口代码中出现$ref
    java获取encodeURI编码后的链接
    剑指 Offer II 002. 二进制加法 模拟法
    Leetcode NO.46 Permutations 全排列
  • 原文地址:https://www.cnblogs.com/silentdoer/p/8456433.html
Copyright © 2020-2023  润新知