手写一个Spring的IOC,你就会知道怎么解决循环依赖

125 阅读2分钟

准备两个类

@Data
public class HelloIoc {
    private int age;
    private String name;
    private User user;
    public void sayHello(){
        System.out.println("my name is"+name);
    }
}
@Data
public class User {
    private int age;
    private String name;
    private String work;
    private HelloIoc helloIoc;

    public void sayHi(){
        System.out.println("我叫"+name);
    }
}

然后,准备一个XML文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean id="HelloIoc" class="org.example.pojo.HelloIoc">
        <property name="name" value="jeysin"/>
        <property name="age" value="13"/>
    </bean>

    <bean id="user" class="org.example.pojo.User">
        <property name="name" value="yize"/>
        <property name="age" value="13" />
    </bean>

</beans>

对XML文件进行解析

读取xml文件的接口

public interface BeanDefinitonReader {

    void loadBeanDefiniton(Resource resource) throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException;

}

实现类:

package org.example.Resouce;

import org.example.Bean.BeanDefiniton;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.swing.text.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Clive
 * @date 2020/9/17 0017 10:34
 */
public class DefaultBeanDefinitonReader implements BeanDefinitonReader {
    private final Map<String, BeanDefiniton> beanDefinitonMap=new HashMap<>();

    public DefaultBeanDefinitonReader(Resource resource) throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException {
        loadBeanDefiniton(resource);
    }

    @Override
    public void loadBeanDefiniton(Resource resource) throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException {
        InputStream inputStream=null;
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
         inputStream = resource.getInputStream();
        org.w3c.dom.Document document = documentBuilder.parse(inputStream);
        registerBeanDefinitons(document);
        inputStream.close();
    }

    public void registerBeanDefinitons(org.w3c.dom.Document document) throws ClassNotFoundException {
        Element rootElement = document.getDocumentElement();
        NodeList childNodes = rootElement.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node instanceof Element){
                Element element = (Element) node;
                String beanId = element.getAttribute("id");
                String beanClass = element.getAttribute("class");
                BeanDefiniton beanDefiniton = new BeanDefiniton(beanClass);
                beanDefiniton.setBeanClass(Class.forName(beanClass));
                processProperties(element,beanDefiniton);
                beanDefinitonMap.put(beanId,beanDefiniton);
            }
        }
    }

    public void processProperties(Element beanNode,BeanDefiniton beanDefiniton){
        NodeList childNodes = beanNode.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node instanceof Element){
                Element element = (Element) node;
                String fieldName = element.getAttribute("name");
                String fieldValue = element.getAttribute("value");
                if (null!=fieldValue && fieldValue.length()>0){
                    beanDefiniton.setBeanMaps(fieldName,fieldValue);
                }
                else{
                    String ref = element.getAttribute("ref");
                    beanDefiniton.setBeanMaps(fieldName,new BeanRef(ref));
                }
            }
        }
    }

    public Map<String, BeanDefiniton> getBeanDefinitonMap() {
        return beanDefinitonMap;
    }
}

读取Resource的接口

public interface Resource {
    public  InputStream getInputStream() ;
}

实现类:

public class UrlResource implements Resource {

    private String configLocation;

    @Override
    public InputStream getInputStream() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(configLocation);
        return resourceAsStream;
    }

    public UrlResource(String configLocation) {
        this.configLocation = configLocation;
    }


}

解决循环依赖,把Ref的类存起来

package org.example.Resouce;

/**
 * @author Clive
 * @date 2020/9/17 0017 10:27
 */
public class BeanRef {
    private String BeanRef;

    public BeanRef(String beanRef) {
        BeanRef = beanRef;
    }

    public String getBeanRef() {
        return BeanRef;
    }
}

解析出来的数据存放在BeanDifinition类中

package org.example.Bean;

import lombok.Data;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Clive
 * @date 2020/9/17 0017 10:13
 */

public class BeanDefiniton {
    private Object bean;
    private Class beanClass;
    private String beanClassName;
    private ConcurrentHashMap<String,Object> beanMaps =new ConcurrentHashMap<String, Object>();

    public ConcurrentHashMap<String, Object> getEarlyBeanMaps() {
        return earlyBeanMaps;
    }

    public void setEarlyBeanMaps(String name,Object s) {
        earlyBeanMaps.put(name,s);
    }

    private ConcurrentHashMap<String,Object> earlyBeanMaps =new ConcurrentHashMap<String, Object>();

    public BeanDefiniton(String beanClassName) {
        this.beanClassName = beanClassName;
    }

    public Object getBean() {
        return bean;
    }

    public void setBean(Object bean) {
        this.bean = bean;
    }

    public Class getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }

    public String getBeanClassName() {
        return beanClassName;
    }

    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
    }

    public ConcurrentHashMap<String, Object> getBeanMaps() {
        return beanMaps;
    }

    public void setBeanMaps(String fieldName, Object fieldValue) {
        beanMaps.put(fieldName,fieldValue);
    }
}

工厂类的接口

/**
 * @author Clive
 * @date 2020/9/17 0017 11:15
 */
public interface BeanFactory {
    Object getBean(String name) throws NoSuchFieldException, IllegalAccessException;
    public void registerBeanDefiniton(String beanName,BeanDefiniton beanDefiniton);
}

实现类:

package org.example.Bean;



import org.example.Resouce.BeanRef;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Clive
 * @date 2020/9/17 0017 11:17
 */
public class DefaultAutowireFactory implements BeanFactory {
    private ConcurrentHashMap<String, BeanDefiniton> beanMaps=new ConcurrentHashMap<>();

    @Override
    public Object getBean(String name) throws NoSuchFieldException, IllegalAccessException {
        BeanDefiniton beanDefiniton = beanMaps.get(name);
        Object bean = beanDefiniton.getBean();
        if (null==bean){
            bean=doCreateBean(beanDefiniton);
        }
        return bean;
    }

    @Override
    public void registerBeanDefiniton(String beanName, BeanDefiniton beanDefiniton) {
        beanMaps.put(beanName,beanDefiniton);
    }

    public Object doCreateBean(BeanDefiniton beanDefiniton) throws NoSuchFieldException, IllegalAccessException {
        Object bean=null;
        try {
            bean = beanDefiniton.getBeanClass().newInstance();

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        beanDefiniton.setBean(bean);
        applyPropertyValue(bean,beanDefiniton.getBeanMaps());
        return bean;
    }

    public void  applyPropertyValue(Object bean, Map<String,Object> propertyMap) throws NoSuchFieldException, IllegalAccessException {
        Set<Map.Entry<String, Object>> entries = propertyMap.entrySet();
        for (Map.Entry<String,Object> entry:
                entries) {
            Field declaredField = bean.getClass().getDeclaredField(entry.getKey());
            declaredField.setAccessible(true);
            Object value = entry.getValue();
            if(value instanceof BeanRef){
                declaredField.set(bean, getBean(((BeanRef) value).getBeanRef()));
            }else {
                String type = declaredField.getType().getName();
                if (type.equals("java.lang.String")){
                    declaredField.set(bean,(String)value);
                }else if (type.equals("java.lang.integer")||type.equals("int")){
                    declaredField.set(bean,Integer.valueOf((String) value));
                }
            }
        }
    }
}

设计一个ApplicationContext类,进行bean的初始化

package org.example.Bean;

/**
 * @author Clive
 * @date 2020/9/17 0017 12:53
 */
public interface ApplicationContext {

    Object getBean(String  beanName) throws NoSuchFieldException, IllegalAccessException;
}

实现类:

package org.example.Bean;

import org.example.Resouce.DefaultBeanDefinitonReader;
import org.example.Resouce.UrlResource;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.Map;

/**
 * @author Clive
 * @date 2020/9/17 0017 12:55
 */
public class ClassPathXmlApplicationContext implements ApplicationContext {
    private String configLocation;
    private BeanFactory beanFactory;
    private static ClassPathXmlApplicationContext context;


    private ClassPathXmlApplicationContext() throws ParserConfigurationException, SAXException, IOException {
        this.beanFactory=new DefaultAutowireFactory();

    }

    public static ClassPathXmlApplicationContext newInstance() throws IOException, SAXException, ParserConfigurationException {
        if(context==null){
            context=new ClassPathXmlApplicationContext();
        }
        return context;
    }

    public void setConfigLocation(String configLocation) throws ParserConfigurationException, SAXException, IOException, ClassNotFoundException {
        this.configLocation = configLocation;
        refresh();
    }

    @Override
    public Object getBean(String beanName) throws NoSuchFieldException, IllegalAccessException {
        return beanFactory.getBean(beanName);
    }

    public void  refresh() throws IOException, SAXException, ParserConfigurationException, ClassNotFoundException {
        DefaultBeanDefinitonReader reader = new DefaultBeanDefinitonReader(new UrlResource(configLocation));
        for (Map.Entry<String, BeanDefiniton> entry :
                reader.getBeanDefinitonMap().entrySet()) {
            this.beanFactory.registerBeanDefiniton(entry.getKey(), entry.getValue());
        }
    }
}

最后测试一下

package org.example;

import static org.junit.Assert.assertTrue;

import org.example.Bean.ApplicationContext;
import org.example.Bean.ClassPathXmlApplicationContext;
import org.example.pojo.HelloIoc;
import org.example.pojo.User;
import org.junit.Test;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;

/**
 * Unit test for simple App.
 */
public class AppTest 
{
    /**
     * Rigorous Test :-)
     */
    @Test
    public void shouldAnswerWithTrue()
    {
        assertTrue( true );
    }

    @Test
    public void test() throws NoSuchFieldException, IllegalAccessException, ParserConfigurationException, SAXException, IOException, ClassNotFoundException {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = ClassPathXmlApplicationContext.newInstance();
        classPathXmlApplicationContext.setConfigLocation("application.xml");
        User user = (User) classPathXmlApplicationContext.getBean("user");
        user.sayHi();
        HelloIoc helloIoc = (HelloIoc) classPathXmlApplicationContext.getBean("HelloIoc");
        helloIoc.sayHello();
    }
}