美文网首页
Spring:简单实现IOC

Spring:简单实现IOC

作者: 侧耳倾听y | 来源:发表于2021-02-21 20:04 被阅读0次

    使用反射实现简单的IOC,目标:
    1.仅支持单例、set方法注入的IOC
    2.解决循环依赖

    解决循环依赖:
    个人认为有两种思路,假设A与B相互依赖:
    1.创建A的时候,发现A需要B,所以再去创建B,引入另外一个map来解决无限循环的问题
    2.将A和B都创建出来,之后再注入A和B所需要的的依赖
    下面xml方式使用第一种思路,注解方式使用第二种思路

    • 使用XML实现IOC

    基本思路:读取xml文件,根据读取的结果,使用反射创建对象实例,并放入静态变量map中。
    该实现的大部分代码,参考了一位同学的代码:https://github.com/HuangFromJYU/JSpring-IoC
    只是我认为他代码里面有两个地方,我做了一些修改:
    1.A对象中注入的B,并不是容器中的B
    2.A与B互相依赖,会导致stackOverFlow
    下面是代码实现:

    读取xml配置

    package mySpring.ioc.xml.reader;
    
    import mySpring.ioc.xml.tagEntity.Bean;
    import mySpring.ioc.xml.tagEntity.Property;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class ConfigurationReader {
    
        public static Map<String, Bean> getBeanConfig(String path) {
            // 存放配置信息,返回结果
            Map<String, Bean> result = new HashMap<String, Bean>();
            // 创建解析器
            SAXReader reader = new SAXReader();
            // 加载配置文件,不以/开头时默认是此类所在包下面取资源,以/开头则是从classpath下获取
            InputStream is = ConfigurationReader.class.getResourceAsStream(path);
            Document doc;
            try {
                doc = reader.read(is);
            } catch (DocumentException e) {
                throw new RuntimeException("加载配置文件出错");
            }
            List<Element> beanNodes = doc.selectNodes("//bean");
            // 遍历所有bean节点,并将信息封装到Bean对象中
            for (Element ele : beanNodes) {
                Bean bean = new Bean();
                bean.setName(ele.attributeValue("name"));
                bean.setClassName(ele.attributeValue("class"));
                // 获取bean节点下所有的property节点
                List<Element> propNodes = ele.elements("property");
                if (propNodes != null) {
                    for (Element prop : propNodes) {
                        Property p = new Property();
                        p.setName(prop.attributeValue("name"));
                        p.setValue(prop.attributeValue("value"));
                        p.setRef(prop.attributeValue("ref"));
                        // 将property添加到所属bean中
                        bean.getProperties().add(p);
                    }
                }
                result.put(bean.getName(), bean);
            }
            return result;
        }
    }
    
    

    xml中的标签对应的实体

    package mySpring.ioc.xml.tagEntity;
    
    import lombok.Getter;
    import lombok.Setter;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Getter
    @Setter
    public class Bean {
    
        private String name;
        private String className;
        private List<Property> properties = new ArrayList<Property>();
    }
    
    package mySpring.ioc.xml.tagEntity;
    
    import lombok.Getter;
    import lombok.Setter;
    
    @Getter
    @Setter
    public class Property {
    
        private String name;
        private String value;
        private String ref;
    }
    
    

    获取Bean的context

    package mySpring.ioc.xml;
    
    import mySpring.ioc.BeanFactory;
    import mySpring.ioc.xml.reader.ConfigurationReader;
    import mySpring.ioc.xml.tagEntity.Bean;
    import mySpring.ioc.xml.tagEntity.Property;
    import org.apache.commons.beanutils.BeanUtils;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class ClassPathXmlApplicationContext implements BeanFactory {
    
        /**
         * 存放单例对象的map
         */
        private static Map<String, Object> singletonMap = new HashMap<>();
    
        /**
         * 用于解决循环依赖
         */
        private static Map<String, Object> earlySingletonMap = new HashMap<>();
    
        private static Map<String, Bean> config;
    
        public ClassPathXmlApplicationContext(String path) {
            config = ConfigurationReader.getBeanConfig(path);
            for (Map.Entry<String, Bean> e : config.entrySet()) {
                // 获取bean信息
                Bean bean = e.getValue();
                // 如果设置成单例的才创建好bean对象放进容器中
                createBeanByConfig(bean);
            }
        }
    
        public Object getBean(String name) {
            return singletonMap.get(name);
        }
    
        private Object createBeanByConfig(Bean bean) {
            // 如果已经单例对象已经在缓存中,则直接返回
            Object beanObj = singletonMap.get(bean.getName());
            if (beanObj != null) {
                return beanObj;
            }
            try {
                Class<?> clazz = Class.forName(bean.getClassName());
                // 创建bean对象
                beanObj = clazz.newInstance();
                // 把半成品对象放入,以备后续使用
                earlySingletonMap.put(bean.getName(), beanObj);
                // 获取bean对象中的property配置
                List<Property> properties = bean.getProperties();
                // 遍历bean对象中的property配置,并将对应的value或者ref注入到bean对象中
                for (Property property : properties) {
                    Map<String, Object> params = new HashMap<>();
                    if (property.getValue() != null) {
                        params.put(property.getName(), property.getValue());
                        // 将value值注入到bean对象中
                        BeanUtils.populate(beanObj, params);
                    }
                    if (property.getRef() != null) {
                        Object ref = singletonMap.get(property.getRef());
                        if (ref == null) {
                            // 查看半成品缓存中是否有需要的实体
                            ref = earlySingletonMap.get(property.getRef());
                            if (ref == null) {
                                ref = createBeanByConfig(config.get(property.getRef()));
                            }
                        }
                        params.put(property.getName(), ref);
                        // 将ref对象注入bean对象中
                        BeanUtils.populate(beanObj, params);
                    }
                }
                earlySingletonMap.remove(bean.getName());
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException();
            }
            singletonMap.put(bean.getName(), beanObj);
            return beanObj;
        }
    }
    

    xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans>
        <bean name="A" class="mySpring.pojo.A">
            <property name="name" value="i am a"/>
            <property name="b" ref="B"/>
        </bean>
    
        <bean name="B" class="mySpring.pojo.B">
            <property name="name" value="i am b"/>
            <property name="c" ref="C"/>
        </bean>
    
        <bean name="C" class="mySpring.pojo.C">
            <property name="name" value="i am c"/>
            <property name="a" ref="A"/>
        </bean>
    </beans>
    

    运行测试

    package mySpring.ioc;
    
    import mySpring.ioc.xml.ClassPathXmlApplicationContext;
    import mySpring.pojo.A;
    import mySpring.pojo.B;
    import mySpring.pojo.C;
    
    public class Runner {
    
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext-ioc.xml");
            A a = (A) context.getBean("A");
            A a1 = (A) context.getBean("A");
            B b = (B) context.getBean("B");
            C c = (C) context.getBean("C");
            System.out.println(a);
            System.out.println(b);
            System.out.println(b);
            System.out.println(a == a1);
            System.out.println(a.getB() == b);
            System.out.println(b.getC() == c);
            System.out.println(c.getA() == a);
        }
    }
    
    
    • 使用注解实现IOC

    基本思路是扫描对应包下加了注解的实体类,并创建对象和注入依赖

    注解类

    package mySpring.ioc.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAutowired {
    }
    
    package mySpring.ioc.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyComponent {
    }
    

    获取对应包下所有的class对象,这个方法写的并不好,是东拼西凑的一个实现。

    package mySpring.ioc.annotation;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.List;
    import java.util.Objects;
    import java.util.stream.Collectors;
    
    public class ClazzUtils {
    
        public static List<Class<?>> getAllClasses(String packageName) {
            List<String> result = new ArrayList<>();
            String suffixPath = packageName.replaceAll("\\.", "/");
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            try {
                Enumeration<URL> urls = loader.getResources(suffixPath);
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    if (url != null) {
                        String protocol = url.getProtocol();
                        if ("file".equals(protocol)) {
                            getAllClassesNames(new File(url.getPath()), result);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result.stream().map(className -> {
                try {
                    return Class.forName(className);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    return null;
                }
            }).collect(Collectors.toList());
        }
    
        private static void getAllClassesNames(File file, List<String> list) {
            if (!file.exists()) {
                return;
            }
            if (file.isDirectory()) {
                for (File listFile : Objects.requireNonNull(file.listFiles())) {
                    getAllClassesNames(listFile, list);
                }
            } else {
                list.add(file.getPath().split("classes.")[1].replace("\\", ".").replace(".class", ""));
            }
        }
    }
    
    

    获取Bean的context

    package mySpring.ioc.annotation;
    
    import mySpring.ioc.BeanFactory;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class AnnotationApplicationContext implements BeanFactory {
    
        private  Map<String, Object> singletonMap = new HashMap<>();
    
        public AnnotationApplicationContext(String packageName) {
            List<Class<?>> classes = ClazzUtils.getAllClasses(packageName);
            for (Class<?> clazz : classes) {
                // 如果加了@MyComponent注解,则实例化
                Annotation annotation = clazz.getAnnotation(MyComponent.class);
                if (annotation != null) {
                    createBean(clazz);
                }
            }
            // 依赖注入
            for (Class<?> clazz : classes) {
                autowired(clazz);
            }
        }
    
    
        @Override
        public Object getBean(String name) {
            return singletonMap.get(name);
        }
    
        /**
         * 利用反射创建实体
         *
         * @param clazz
         */
        private void createBean(Class<?> clazz) {
            String name = clazz.getSimpleName();
            Object o = singletonMap.get(name);
            if (o == null) {
                try {
                    o = clazz.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                singletonMap.put(name, o);
            }
        }
    
        /**
         * 给实例化后的对象,依赖注入
         *
         * @param clazz
         */
        private void autowired(Class<?> clazz) {
            Object o = singletonMap.get(clazz.getSimpleName());
            List<Field> fields = getAutowired(clazz);
            if (fields.size() > 0) {
                for (Field field : fields) {
                    field.setAccessible(true);
                    Object diObject = singletonMap.get(field.getType().getSimpleName());
                    if (diObject == null) {
                        throw new RuntimeException();
                    }
                    try {
                        field.set(o, diObject);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 获取全部需要注入的字段
         *
         * @param clazz
         * @return
         */
        private List<Field> getAutowired(Class<?> clazz) {
            List<Field> list = new ArrayList<>();
            Field[] fields = clazz.getDeclaredFields();
            if (fields.length > 0) {
                for (Field field : fields) {
                    MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                    if (annotation != null) {
                        list.add(field);
                    }
                }
            }
            return list;
        }
    }
    

    运行测试

    package mySpring.ioc;
    
    import mySpring.ioc.annotation.AnnotationApplicationContext;
    import mySpring.ioc.xml.ClassPathXmlApplicationContext;
    import mySpring.pojo.A;
    import mySpring.pojo.B;
    import mySpring.pojo.C;
    
    public class Runner {
    
        public static void main(String[] args) {
    //        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext-ioc.xml");
            AnnotationApplicationContext context = new AnnotationApplicationContext("mySpring.pojo");
            A a = (A) context.getBean("A");
            A a1 = (A) context.getBean("A");
            B b = (B) context.getBean("B");
            C c = (C) context.getBean("C");
            System.out.println(a);
            System.out.println(b);
            System.out.println(b);
            System.out.println(a == a1);
            System.out.println(a.getB() == b);
            System.out.println(b.getC() == c);
            System.out.println(c.getA() == a);
        }
    }
    
    • 前面两种实现方式,用到一些公共的类,下面贴出来

    beanFactory接口

    package mySpring.ioc;
    
    public interface BeanFactory {
    
        Object getBean(String name);
    }
    

    实体类

    package mySpring.pojo;
    
    import lombok.Getter;
    import lombok.Setter;
    import mySpring.ioc.annotation.MyAutowired;
    import mySpring.ioc.annotation.MyComponent;
    
    @Getter
    @Setter
    @MyComponent
    public class A {
    
        private String name;
    
        @MyAutowired
        private B b;
    }
    
    package mySpring.pojo;
    
    import lombok.Getter;
    import lombok.Setter;
    import mySpring.ioc.annotation.MyAutowired;
    import mySpring.ioc.annotation.MyComponent;
    
    @Getter
    @Setter
    @MyComponent
    public class B {
    
        private String name;
    
        @MyAutowired
        private C c;
    }
    
    package mySpring.pojo;
    
    import lombok.Getter;
    import lombok.Setter;
    import mySpring.ioc.annotation.MyAutowired;
    import mySpring.ioc.annotation.MyComponent;
    
    @Getter
    @Setter
    @MyComponent
    public class C {
    
        private String name;
    
        @MyAutowired
        private A a;
    }
    

    我把代码放到了github上:https://github.com/wxyyrain/spring-study/tree/master/spring-homework/src/main
    有什么不对的地方,欢迎大家交流讨论

    相关文章

      网友评论

          本文标题:Spring:简单实现IOC

          本文链接:https://www.haomeiwen.com/subject/hjsjxltx.html