手动模拟Spring(一)

1.写在前面

由于上次看spring的源码的时候已经是半年前了,笔者对spring的源码只记得一个大概了,于是我打算重新看一次spring的源码,看spring的源码之前最好的方式就是模拟一遍spring的,但是奈何我的水平有限,于是就去网上找现成的例子,于是笔者找到了GitHub上的一个项目《tiny-spring》,本系列的博客主要是参考这个开源项目来写的,加上自己的一些理解。

2.step1最基本的容器。

看过我的spring源码的系列的专题,都知道spring的Bean的描述信息都是通过一个类BeanDefinition接口来描述,于是我这儿也创建一个BeanDefinition类。具体的代码如下:

package com.ys.tinyioc;

public class BeanDefinition {

    private Object bean;

    public BeanDefinition(Object bean){
        this.bean = bean;
    }

    public Object getBean() {
        return bean;
    }
}

我们都知道Bean是通过Bean工厂创建的,同时所有BeanDefinition存在spring容器的一个Map中,于是我就有了对应的思路,创建对应BeanFactory类,具体的代码如下:

package com.ys.tinyioc;

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

public class BeanFactory {

  //存放所有的BeanDefinition
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

  //获取Bean,就是在Map中通过name获取这个BeanDefinition,然后获取这个Bean
    public Object getBean(String name) {
        return beanDefinitionMap.get(name).getBean();
    }

  //注册BeanDefinition就是往Map中存放一个键值对
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(name, beanDefinition);
    }
}

这样我们一个最简单的容器就构建完成。我们再来写一个BeanDefinition,然后把它添加到容器中,测试一下,具体的代码如下:

package com.ys.tinyioc;

public class HelloWorldService {

    public void helloWorld(){
        System.out.println("Hello World");
    }
}

测试类如下:

package com.ys.tinyioc;

import org.junit.Test;

public class BeanFactoryTest {

    @Test
    public void test() {

        //1.初始化BeanFactory
        BeanFactory beanFactory = new BeanFactory();

        //2.注入Bean
        BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());
        beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);

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

运行结果如下:

在这里插入图片描述

通过我们的运行结果,我们可以得出我们书写的代码是对的,一个最基本的容器就构建完成。可以你发现这个Bean是我们创建好的放到Bean工厂的,那么Bean工厂就失去它的职责了,创建Bean,实际使用中,我们希望容器来管理bean的创建。于是我们将bean的初始化放入BeanFactory中。为了保证扩展性,我们使用Extract Interface的方法,将BeanFactory替换成接口,而使用AbstractBeanFactoryAutowireCapableBeanFactory作为其实现。

3.step2将bean创建放入工厂

我们先要修改BeanDefinition这个类,要添加几个属性,让其能够通过反射来创建对应的Bean,于是修改后代码如下:

package com.ys.ioc;

public class BeanDefinition {

    private Object bean;

    private Class beanClass;

    private String beanClassName;

    public BeanDefinition() {
    }

    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;
    }

  	//设置BeanClassName的同时,将beanClass的属性设置上
    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
        try {
            this.beanClass = Class.forName(beanClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

修改完BeanDefinition类后,我们需要修改BeanFactory类,将这个类改成接口,抽象出两个方法(获取Bean,注册BeanDefinition),具体的代码如下:

package com.ys.ioc.factory;

import com.ys.ioc.BeanDefinition;

public interface BeanFactory {

    Object getBean(String name);

    void registerBeanDefinition(String name, BeanDefinition beanDefinition);
}

既然这两个方法是两个最基本的方法,而其他的工厂有这两个方法,同时可能有其他的方法,于是我这儿将这两个方法通过一个抽象类来实现,其他的工厂如果要扩展只需要继承这个抽象工厂类,具体的如下:

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 name) {
        return beanDefinitionMap.get(name).getBean();
    }

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

    //初始化Bean
    protected abstract Object doCreateBean(BeanDefinition beanDefinition);
}

和原来的差不多,只不过将Bean的创建交给了BeanFactory,同时抽象出来一个创建Bean的方法,由对应的子类去实现,于是有了下面的实现。

package com.ys.ioc.factory;

import com.ys.ioc.BeanDefinition;

public class AutowireCapableBeanFactory extends AbstractBeanFactory{
    @Override
    protected Object doCreateBean(BeanDefinition beanDefinition) {
        try {
            Object bean = beanDefinition.getBeanClass().newInstance();
            return bean;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

其实就是通过反射的方式来创建对应的Bean,我们再来测试一下,写出如下的Bean,具体的代码如下:

package com.ys.ioc;

public class HelloWorldService {

    public void helloWorld(){
        System.out.println("hello World");
    }
}

测试类的代码如下:

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(){
        //1.初始化beanFactory
        BeanFactory beanFactory = new AutowireCapableBeanFactory();

        //2.注入bean
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setBeanClassName("com.ys.ioc.HelloWorldService");
        beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);

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

运行结果如下:

在这里插入图片描述

可以发现我们的运行的结果没有问题,但是我们创建的Bean没有任何属性,于是有了第三步的属性的注入。

3.step3为Bean注入属性

既然有属性注入,那我们就要清楚这些属性通过什么来存,于是我打算创建一个类PropertyValue用来Bean的属性,具体的代码如下:

package com.ys.ioc;

public class PropertyValue {

    private String name;

    private Object value;

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

    public String getName() {
        return name;
    }

    public Object getValue() {
        return value;
    }
}

但是一个Bean的属性不止一个,所有我们这儿也需要一个类PropertyValues来存Bean中的属性,可能有人会说为什么不直接用List来存呢?因为这儿我们可能对PropertyValue进行一些操作,所以用PropertyValues来存,具体的代码如下:

package com.ys.ioc;

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

// 包装一个对象所有的PropertyValue。<br/>
// 为什么封装而不是直接用List?因为可以封装一些操作。
public class PropertyValues {

    private final List<PropertyValue> propertyValueList = new ArrayList<>();

    public void addPropertyValue(PropertyValue pv) {
        this.propertyValueList.add(pv);
    }

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

我这儿没有对PropertyValue进行了一些操作,主要是为了简单,如果你们想对PropertyValue属性进行操作,只需要在addPropertyValue方法中进行一些操作。既然封装了Bean属性的类,那么这儿BeanDefinition的代码也需要修改了,具体的代码如下:

package com.ys.ioc;

//bean的内容及元数据,保存在BeanFactory中,包装bean的实体
public class BeanDefinition {

    private Object bean;

    private Class beanClass;

    private String beanClassName;

    private PropertyValues propertyValues;

    public BeanDefinition() {
    }

    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;
        try {
            this.beanClass = Class.forName(beanClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public PropertyValues getPropertyValues() {
        return propertyValues;
    }

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

在原来的基础上添加了PropertyValues(Bean的属性)然后BeanFactoryAbstractBeanFactory都没有变化,只需要AutowireCapableBeanFactory类,将Bean的属性注入即可,具体的代码如下:

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 beanDefinition) throws Exception {
        Object bean = createBeanInstance(beanDefinition);
      //设置属性
        applyPropertyValues(bean, beanDefinition);
        return bean;
    }

    protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
      //遍历所有的属性通过反射的方式设置给Bean
        for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
            Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
            declaredField.setAccessible(true);
            declaredField.set(bean, propertyValue.getValue());
        }
    }

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

至此代码就写完了,我们来写测试类,具体的代码如下,首先是我们的Bean

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;
    }
}

然后是我们的测试类,具体代码如下:

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();

	}
}

运行的结果如下:

在这里插入图片描述

可以发现我们的属性是注入进去了。

4.写在最后

今天笔者就简单的模拟spring的IOC的容器,当然spring的IOC容器肯定不止这些,还有读取xml配置来初始化bean,为bean注入bean,以及ApplicationContext,我会在后面的博客介绍,今天就先这样吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值