Spring:简单实现IOC

2021-02-21  本文已影响0人  侧耳倾听y

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

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

基本思路:读取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);
    }
}

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

注解类

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
有什么不对的地方,欢迎大家交流讨论

上一篇下一篇

猜你喜欢

热点阅读