手动模拟Spring(二)

1.写在前面

我之前已经手动模拟Spring的源码,但是只是IOC的一小部分,今天打算将剩下的IOC的一部分给模拟完,尽量做到和Spring的IOC的容器做到差不多,就是基础的功能有,没有Spring的IOC的源码做的那么好,因为Spring的源码是真的太好了。

2.step4-读取xml配置来初始化bean

前面我模拟的Spring的IOC容器,已经可以支持简单的Bean的生成,同时也支持属性的注入了。但是这还完全布不够,我们都知道早期的Spring是支持读取xml文件的。我们先来看下我们步骤三的测试的代码,具体的代码如下:

package com.ys.ioc;

import com.ys.ioc.factory.AutowireCapableBeanFactory;
import com.ys.ioc.factory.BeanFactory;
import org.junit.Test;

public class BeanFactoryTest {

	@Test
	public void test() throws Exception {
		// 1.初始化BeanFactory
		BeanFactory beanFactory = new AutowireCapableBeanFactory();

		// 2.Bean定义
		BeanDefinition beanDefinition = new BeanDefinition();
		beanDefinition.setBeanClassName("com.ys.ioc.HelloWorldService");

		// 3.设置属性
		PropertyValues propertyValues = new PropertyValues();
		propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!"));
    beanDefinition.setPropertyValues(propertyValues);

		// 4.生成Bean
		beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);

		// 5.获取Bean
		HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
		helloWorldService.helloWorld();

	}
}

这段初始化的代码是又臭又长,当然Spring也支持这种方式的编程,但是这种方式写出来的BeanDefinition太多太长了,不好维护,那么Spring提供了什么解决方案呢?Spring提供了两种解决办法,一种是通过xml的方式来定义BeanDefinition,一种是通过注解来定义BeanDefinition。笔者就模拟通过xml的方式来定义BeanDefinition。首先我们还是先来看下BeanDefinition类,具体的代码如下:

package com.ys.ioc;

public class BeanDefinition {

    private Object bean;

    private String beanClassName;

    private PropertyValues propertyValues = new PropertyValues();

    private Class beanClass;

    public BeanDefinition() {
    }

    public Object getBean() {
        return bean;
    }

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

    public String getBeanClassName() {
        return beanClassName;
    }

    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
        try {
            this.beanClass = Class.forName(beanClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public PropertyValues getPropertyValues() {
        return propertyValues;
    }

    public void setPropertyValues(PropertyValues propertyValues) {
        this.propertyValues = propertyValues;
    }

    public Class getBeanClass() {
        return beanClass;
    }

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

BeanDefinition类和原来是一样的,继续看下PropertyValuesPropertyValue的代码,具体的代码如下:

package com.ys.ioc;

public class PropertyValue {

    private final String name;

    private final Object value;

    public PropertyValue(String name, Object value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public Object getValue() {
        return value;
    }
}

package com.ys.ioc;

import java.util.ArrayList;
import java.util.List;

public class PropertyValues {

    private List<PropertyValue> propertyValues = new ArrayList<>();

    public void addPropertyValue(PropertyValue propertyValue){
        this.propertyValues.add(propertyValue);
    }

    public List<PropertyValue> getPropertyValues() {
        return this.propertyValues;
    }
}

这两个类和原来的还是一样的。没有任何的变化。我们再来看下BeanFactory接口的定义,具体的如下:

package com.ys.ioc.factory;

import com.ys.ioc.BeanDefinition;

public interface BeanFactory {

    Object getBean(String beanName);

    void registerBeanDefinition(String name, BeanDefinition definition) throws Exception;
}

同样也是没有变化的。我们再来看下AbstractBeanFactory类,这是一个抽象类,同时是实现BeanFactory接口的,具体的代码如下:

package com.ys.ioc.factory;

import com.ys.ioc.BeanDefinition;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class AbstractBeanFactory implements BeanFactory {

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    @Override
    public Object getBean(String beanName) {
        return beanDefinitionMap.get(beanName).getBean();
    }

    @Override
    public void registerBeanDefinition(String name, BeanDefinition definition) throws Exception{
        Object bean = doCreateBean(definition);
        definition.setBean(bean);
        beanDefinitionMap.put(name,definition);
    }

    protected abstract Object doCreateBean(BeanDefinition definition) throws Exception;
}

代码和原来的是一样的,我们再来看下AutowireCapableBeanFactory,这个类继承AbstractBeanFactory,具体的代码如下:

package com.ys.ioc.factory;

import com.ys.ioc.BeanDefinition;
import com.ys.ioc.PropertyValue;

import java.lang.reflect.Field;

public class AutowireCapableBeanFactory extends AbstractBeanFactory {

    @Override
    protected Object doCreateBean(BeanDefinition definition) throws Exception {
        Object bean = createBeanInstance(definition);
        applyPropertyValues(bean, definition);
        return bean;
    }

    private void applyPropertyValues(Object bean, BeanDefinition definition) throws Exception {
        for (PropertyValue propertyValue : definition.getPropertyValues().getPropertyValues()) {
            Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
            declaredField.setAccessible(true);
            declaredField.set(bean,propertyValue.getValue());
        }
    }

    private Object createBeanInstance(BeanDefinition definition) throws Exception {
        return definition.getBeanClass().newInstance();
    }
}

代码还是和原来的一样的。下面就要看我们读取xml文件的代码了,我们先将我们的资源抽成一个接口Resource,具体的代码如下:

package com.ys.ioc.io;

import java.io.InputStream;

public interface Resource {

    InputStream getInputStream()throws Exception;
  
}

我们在写一个UrlResource来实现这个Resource接口,具体的代码如下:

package com.ys.ioc.io;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class UrlResource implements Resource {

    private final URL url;

    public UrlResource(URL url) {
        this.url = url;
    }

    @Override
    public InputStream getInputStream() throws Exception {
        URLConnection urlConnection = url.openConnection();
        urlConnection.connect();
        return urlConnection.getInputStream();
    }
}

这个类主要是通过我们给的url,来获取输入流。然后我们再来写一个ResourceLoader用来获取传入一个地址,来获取Resource,具体的代码如下:

package com.ys.ioc.io;

import java.net.URL;

public class ResourceLoader {

    public Resource getResource(String location) {
        URL resource = this.getClass().getClassLoader().getResource(location);
        return new UrlResource(resource);
    }
}

通过url来获取输入流,这部分的代码已经写完了,接下来就来写我们的读取器,就是读取xml的配置文件解析成BeanDefinition,先看我们的定义的读取器BeanDefinitionReader的接口定义,具体的代码如下:

package com.ys.ioc;

public interface BeanDefinitionReader {

    void loadBeanDefinitions(String location) throws Exception;
}

我们先定义一个抽象类AbstractBeanDefinitionReader来实现BeanDefinitionReader,具体的代码如下:

package com.ys.ioc;

import com.ys.ioc.io.ResourceLoader;

import java.util.HashMap;
import java.util.Map;

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {

  //用来存从配置文件中读取的BeanDefinition
    private Map<String,BeanDefinition> registry;

    private ResourceLoader resourceLoader;

    public AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
        this.registry = new HashMap<>();
        this.resourceLoader = resourceLoader;
    }


    public Map<String, BeanDefinition> getRegistry() {
        return registry;
    }

    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }

}

下面我们就来实现真正读取xml文件的和解析xml文件的类了,具体的代码如下:

package com.ys.ioc.xml;

import com.ys.ioc.AbstractBeanDefinitionReader;
import com.ys.ioc.BeanDefinition;
import com.ys.ioc.PropertyValue;
import com.ys.ioc.io.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
        super(resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(String location) throws Exception {
      //获取给定文件的输入流
        InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
      //加载BeanDefinition
        doLoadBeanDefinitions(inputStream);
    }

    protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        Document document = documentBuilder.parse(inputStream);
        //解析bean
        registerBeanDefinitions(document);
        inputStream.close();
    }

    private void registerBeanDefinitions(Document document) {
        Element root = document.getDocumentElement();
      //解析bean,从根节点开始
        parseBeanDefinitions(root);
    }

    private void parseBeanDefinitions(Element root) {
      //获取所有的子节点
        NodeList childNodes = root.getChildNodes();
      //遍历
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node item = childNodes.item(i);
            if (item instanceof Element) {
                Element element = (Element) item;
              //继续向下解析
                processBeanDefinition(element);
            }
        }
    }

    private void processBeanDefinition(Element element) {
        String name = element.getAttribute("name");
        String className = element.getAttribute("class");
        BeanDefinition beanDefinition = new BeanDefinition();
      //解析属性
        processProperty(element, beanDefinition);
        beanDefinition.setBeanClassName(className);
        getRegistry().put(name, beanDefinition);
    }

    private void processProperty(Element element, BeanDefinition beanDefinition) {
        NodeList property = element.getElementsByTagName("property");
        for (int i = 0; i < property.getLength(); i++) {
            Node node = property.item(i);
            if (node instanceof Element) {
                Element propertyEle = (Element) node;
                String name = propertyEle.getAttribute("name");
                String value = propertyEle.getAttribute("value");
                beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
            }
        }
    }
}

上面的代码就是解析xml文件,然后将对应的值,设置到BeanDefinition中去,至此整个读取和解析xml文件就讲完了。

接下来就去书写我们的测试类,首先我们先来测试ResourceLoader,具体的测试代码如下:

package com.ys.ioc.io;

import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

public class ResourceLoaderTest {

	@Test
	public void test() throws Exception {
		ResourceLoader resourceLoader = new ResourceLoader();
        Resource resource = resourceLoader.getResource("tinyioc.xml");
        InputStream inputStream = resource.getInputStream();
        Assert.assertNotNull(inputStream);
    }
}

这儿我们利用了断言,判断这个输入流不为空,这儿如果测试通过了,这儿控制台就不会有异常。具体的测试结果如下:

在这里插入图片描述

可以看到我们的测试是通过的,证明我们写的代码是没有问题的。还是来看下配置文件吧,具体的代码如下:

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

    <bean name="helloWorldService" class="com.ys.ioc.HelloWorldService">
        <property name="text" value="Hello World!"></property>
    </bean>

</beans>

然后再来看下HelloWorldService这个类,具体的代码如下:

package com.ys.ioc;

public class HelloWorldService {

    private String text;

    public void helloWorld(){
        System.out.println(text);
    }

    public void setText(String text) {
        this.text = text;
    }
}

这个时候,我们需要来测试一下,我们的XmlBeanDefinitionReader,测试的代码如下:

package com.ys.ioc.xml;

import com.ys.ioc.BeanDefinition;
import com.ys.ioc.io.ResourceLoader;
import org.junit.Assert;
import org.junit.Test;

import java.util.Map;

public class XmlBeanDefinitionReaderTest {

	@Test
	public void test() throws Exception {
		XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
		xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
		Map<String, BeanDefinition> registry = xmlBeanDefinitionReader.getRegistry();
		Assert.assertTrue(registry.size() > 0);
	}
}

上面也是利用了断言,判断读取到BeanDefinition的长度大于0,这儿如果没有问题,控制台应该是通过的,具体的测试结果如下:

在这里插入图片描述

这个时候,需要测试我们写的核心代码了,具体的测试代码如下:

package com.ys.ioc;

import com.ys.ioc.factory.AutowireCapableBeanFactory;
import com.ys.ioc.factory.BeanFactory;
import com.ys.ioc.io.ResourceLoader;
import com.ys.ioc.xml.XmlBeanDefinitionReader;
import org.junit.Test;

import java.util.Map;

public class BeanFactoryTest {

	@Test
	public void test() throws Exception {
		// 1.读取配置
		XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
		xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");

		// 2.初始化BeanFactory并注册bean
		BeanFactory beanFactory = new AutowireCapableBeanFactory();
		for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
			beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
		}

		// 3.获取bean
		HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
		helloWorldService.helloWorld();

	}
}

测试的结果如下:

在这里插入图片描述

可以看到这儿我们测试是成功了。至此我们的第四步就完成了。

3.为bean注入bean

使用xml配置之后,似乎里我们熟知的Spring更近了一步!但是现在有一个大问题没有解决:我们无法处理bean之间的依赖,无法将bean注入到bean中,所以它无法称之为完整的IOC容器!如何实现呢?我们定义一个BeanReference,来表示这个属性是对另一个bean的引用。这个在读取xml的时候初始化,并在初始化bean的时候,进行解析和真实bean的注入。BeanReference代码如下:

package com.ys.ioc;

public class BeanReference {

    private String name;

    private Object bean;

    public BeanReference(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public Object getBean() {
        return bean;
    }

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

接下来我们只需要看下变换的部分,由于篇幅的原因,不变的我这儿就不展示了,同时为了解决循环依赖的问题,我们使用lazy-init的方式,将createBean的事情放到getBean的时候才执行,备注:原来是在registerBeanDefinition方法中,是不是一下子方便很多?这样在注入bean的时候,如果该属性对应的bean找不到,那么就先创建!因为总是先创建后注入,所以不会存在两个循环依赖的bean创建死锁的问题。具体的代码如下:

package com.ys.ioc.factory;

import com.ys.ioc.BeanDefinition;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class AbstractBeanFactory implements BeanFactory{

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

  //用来存所有的bean的name
    private final List<String> beanDefinitionNames = new ArrayList<>();

    @Override
    public Object getBean(String name) throws Exception {
      //获取BeanDefinition
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if (beanDefinition == null){
            throw new IllegalArgumentException("No bean named " + name + " is defined");
        }
      //获取BeanDefinition对应的Bean
        Object bean = beanDefinition.getBean();
      //如果bean为空,我们就出创建
        if (bean == null){
            bean = doCreateBean(beanDefinition);
        }
        return bean;
    }

    @Override
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
        beanDefinitionMap.put(name, beanDefinition);
        beanDefinitionNames.add(name);
    }

    protected abstract Object doCreateBean(BeanDefinition beanDefinition) throws Exception;

    public void preInstantiateSingletons() throws Exception{
        for (Iterator<String> it = this.beanDefinitionNames.iterator(); it.hasNext();){
            String beanName = it.next();
            getBean(beanName);
        }
    }
}

package com.ys.ioc.factory;

import com.ys.ioc.BeanDefinition;
import com.ys.ioc.BeanReference;
import com.ys.ioc.PropertyValue;

import java.lang.reflect.Field;

public class AutowireCapableBeanFactory extends AbstractBeanFactory{

    @Override
    protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception{
        Object bean = createBeanInstance(beanDefinition);
        beanDefinition.setBean(bean);
        applyPropertyValues(bean, beanDefinition);
        return bean;
    }

    private Object createBeanInstance(BeanDefinition beanDefinition) throws Exception{
        return beanDefinition.getBeanClass().newInstance();
    }

    private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception{
        for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()){
            Field field = bean.getClass().getDeclaredField(propertyValue.getName());
            field.setAccessible(true);
            Object value = propertyValue.getValue();
          //为bean注入bean
            if (value instanceof BeanReference){
                BeanReference beanReference = (BeanReference) value;
                value = getBean(beanReference.getName());
            }
            field.set(bean,value);
        }
    }
}

至此代码就修改完成了,于是开始测试,我们先来看下配置文件,具体的配置文件如下:

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

    <bean name="outputService" class="com.ys.ioc.OutputService">
        <property name="helloWorldService" ref="helloWorldService"></property>
    </bean>

    <bean name="helloWorldService" class="com.ys.ioc.HelloWorldService">
        <property name="text" value="Hello World!"></property>
        <property name="outputService" ref="outputService"></property>
    </bean>

</beans>

然后再看我们在配置文件中定义的两个bean,具体的代码如下:

package com.ys.ioc;

import org.junit.Assert;

public class OutputService {

    private HelloWorldService helloWorldService;

    public void output(String text){
        Assert.assertNotNull(helloWorldService);
        System.out.println(text);
    }

    public void setHelloWorldService(HelloWorldService helloWorldService) {
        this.helloWorldService = helloWorldService;
    }
}

package com.ys.ioc;

public class HelloWorldService {

    private String text;

    private OutputService outputService;
  
  	public HelloWorldService() {
        System.out.println("HelloWorldService.HelloWorldService");
    }

    public void helloWorld(){
        outputService.output(text);
    }

    public void setText(String text) {
        this.text = text;
    }

    public void setOutputService(OutputService outputService) {
        this.outputService = outputService;
    }
}

最后看下我们的测试代码:

package com.ys.ioc;

import com.ys.ioc.factory.AbstractBeanFactory;
import com.ys.ioc.factory.AutowireCapableBeanFactory;
import com.ys.ioc.io.ResourceLoader;
import com.ys.ioc.xml.XmlBeanDefinitionReader;
import org.junit.Test;

import java.util.Map;

public class BeanFactoryTest {

    @Test
    public void testLazy() throws Exception {
        // 1.读取配置
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
        xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");

        // 2.初始化BeanFactory并注册bean
        AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
            beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
        }
        System.out.println("before getBean()");

        // 3.获取bean
        HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
        System.out.println("after getBean()");
        helloWorldService.helloWorld();
    }

	@Test
	public void testPreInstantiate() throws Exception {
		// 1.读取配置
		XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
		xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");

		// 2.初始化BeanFactory并注册bean
		AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
		for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
			beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
		}

        // 3.初始化bean
        beanFactory.preInstantiateSingletons();

		// 4.获取bean
		HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
		helloWorldService.helloWorld();
	}
}

上面的测试类第一个方法是测试懒加载,只有在调用getBean的时候这个类才会创建,于是我在HelloWorldService初始化的方法中添加一行打印语句,看调用registerBeanDefinition方法后,bean有没有创建,具体的测试结果如下:

在这里插入图片描述

从测试结果中可以得出我们的懒加载是起作用了。于是我们测试第二个方法,就是初始化bean的方法。由于我们调用的helloWorld方法,是调用outputService.output(text);而这个方法中我们加了断言,表示helloWorldService不为空。这也是一个循环依赖的例子,只要这个例子通过了,那我们的IOC容器中的bean注入bean,就可以了,测试结果如下:

在这里插入图片描述

从测试结果,可以得出我们的IOC的容器已经差不多了。

4.step6-ApplicationContext登场

现在BeanFactory的功能齐全了,但是使用起来有点麻烦。于是我们引入熟悉的ApplicationContext接口,并在AbstractApplicationContextrefresh()方法中进行bean的初始化工作。ApplicationContext接口的代码如下:

package com.ys.ioc.context;

import com.ys.ioc.beans.factory.BeanFactory;

public interface ApplicationContext extends BeanFactory {
}

同时我们对BeanFactory接口也做了一些修改,具体的代码如下:

package com.ys.ioc.beans.factory;

public interface BeanFactory {

    Object getBean(String name) throws Exception;
}

接下来我们再来看AbstractApplicationContext抽象类的代码,具体的代码如下:

package com.ys.ioc.context;

import com.ys.ioc.beans.factory.AbstractBeanFactory;

public abstract class AbstractApplicationContext implements ApplicationContext{

    protected AbstractBeanFactory beanFactory;

    public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public void refresh() throws Exception{}

    @Override
    public Object getBean(String name) throws Exception {
        return beanFactory.getBean(name);
    }
}

再书写ClassPathXmlApplicationContext类对AbstractApplicationContext进行继承,同时完成refresh方法的开发,具体的代码如下:

package com.ys.ioc.context;

import com.ys.ioc.beans.BeanDefinition;
import com.ys.ioc.beans.factory.AbstractBeanFactory;
import com.ys.ioc.beans.factory.AutowireCapableBeanFactory;
import com.ys.ioc.beans.io.ResourceLoader;
import com.ys.ioc.beans.xml.XmlBeanDefinitionReader;

import java.util.Map;

public class ClassPathXmlApplicationContext extends AbstractApplicationContext {

    private String configLocation;

    public ClassPathXmlApplicationContext(String configLocation) throws Exception {
        this(configLocation, new AutowireCapableBeanFactory());
    }

    public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
        super(beanFactory);
        this.configLocation = configLocation;
        refresh();
    }

    @Override
    public void refresh() throws Exception {
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
        xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
            beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
        }
    }
}

这样我们这部分的代码就书写完成了,继续看我们测试的部分,原来的配置文件没有变,我们只需要看测试类,具体的代码如下:

package com.ys.ioc.context;

import com.ys.ioc.HelloWorldService;
import org.junit.Test;

public class ApplicationContextTest {

    @Test
    public void test() throws Exception {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
        HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
        helloWorldService.helloWorld();
    }
}

测试结果如下:

在这里插入图片描述

可以看到我们的测试是没有问题的。

5.写在最后

至此我们简单模拟Spring的IOC容器的源码就完成了,后面会模拟Aop。本篇博客主要参考《tiny-spring》开源项目书写。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值