五分钟,手撸一个简单的Spring容器

工厂和Spring容器
Spring是一个成熟的框架,为了满足扩展性、实现各种功能,所以它的实现如同枝节交错的大树一样,现在让我们把视线从Spring本身移开,来看看一个萌芽版的Spring容器怎么实现。

Spring的IOC本质就是一个大工厂,我们想想一个工厂是怎么运行的呢?
————————————————

生产产品:一个工厂最核心的功能就是生产产品。在Spring里,不用Bean自己来实例化,而是交给Spring,应该怎么实现呢?——答案毫无疑问,反射。

那么这个厂子的生产管理是怎么做的?你应该也知道——工厂模式。

库存产品:工厂一般都是有库房的,用来库存产品,毕竟生产的产品不能立马就拉走。Spring我们都知道是一个容器,这个容器里存的就是对象,不能每次来取对象,都得现场来反射创建对象,得把创建出的对象存起来。

订单处理:还有最重要的一点,工厂根据什么来提供产品呢?订单。这些订单可能五花八门,有线上签签的、有到工厂签的、还有工厂销售上门签的……最后经过处理,指导工厂的出货。

在Spring里,也有这样的订单,它就是我们bean的定义和依赖关系,可以是xml形式,也可以是我们最熟悉的注解形式。
————————————————
涉及到需要的事物有:

订单:Bean定义

Bean可以通过一个配置文件定义,我们会把它解析成一个类型。

userDao = com.example.apidemo.spring.UserDao

BeanConfig.java

bean定义类,配置文件中bean定义对应的实体

package com.example.apidemo.spring;

import lombok.Data;

/**
 * bean定义类,配置文件中bean定义对应的实体
 */
@Data
public class BeanConfig {

    private String beanName;

    private Class  beanClass;
     //省略getter、setter  
}

获取订单:资源加载

接下订单之后,就要由销售向生产部门交接,让生产部门知道商品的规格、数量之类。

资源加载器,就是来完成这个工作的,由它来完成配置文件中配置的加载。

/**
 * 获取订单:资源加载
 * @return
 */
public class ResourceLoader {

    public static Map<String, BeanConfig> getResource() {
        Map<String, BeanConfig> beanDefinitionMap = new HashMap<>(16);
        Properties properties = new Properties();
        try {
            //配置beans.properties文件: userDao = com.example.apidemo.spring.UserDao
            InputStream inputStream = ResourceLoader.class.getResourceAsStream("/beans.properties");
            properties.load(inputStream);
            Iterator<String> it = properties.stringPropertyNames().iterator();
            while (it.hasNext()) {
                String key = it.next();
                String className = properties.getProperty(key);
                BeanConfig beanConfig = new BeanConfig();
                beanConfig.setBeanName(key);
                Class clazz = Class.forName(className);
                beanConfig.setBeanClass(clazz);
                beanDefinitionMap.put(key, beanConfig);
            }
            inputStream.close();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return beanDefinitionMap;
    }

}

订单分配:Bean注册

对象注册器,这里用于单例bean的缓存,我们大幅简化,默认所有bean都是单例的。可以看到所谓单例注册,也很简单,不过是往HashMap里存对象。

/**
 * 仓库:订单分配:Bean注册
 */
public class BeanRegister {

    //单例Bean缓存
    private Map<String, Object> singletonMap = new HashMap<>(32);

    /**
     * 获取单例Bean
     *
     * @param beanName bean名称
     * @return
     */
    public Object getSingletonBean(String beanName) {
        return singletonMap.get(beanName);
    }

    /**
     * 注册单例bean
     *
     * @param beanName
     * @param bean
     */
    public void registerSingletonBean(String beanName, Object bean) {
        if (singletonMap.containsKey(beanName)) {
            return;
        }
        singletonMap.put(beanName, bean);
    }

}

生产车间:对象工厂

好了,到了我们最关键的生产部门了,在工厂里,生产产品的是车间,在IOC容器里,生产对象的是BeanFactory。

  • 对象工厂,我们最核心的一个类,在它初始化的时候,创建了bean注册器,完成了资源的加载。

  • 获取bean的时候,先从单例缓存中取,如果没有取到,就创建并注册一个bean

/**
 * 生产车间:对象工厂
 * 流程:《生产车间:对象工厂》==执行BeanFactory()初始化方法,ResourceLoader().getResource()去==《获取订单:资源加载》
 *      返回一个BeanFactory,里面包含配置的bean实例。 如果第一次调用,就是==《仓库:订单分配:Bean注册》,用BeanRegister去注册一个单例的bean,
 *      并且存储到BeanRegister,如果第二次调用,先从缓存beanRegister取,获取不到再注册。
 */
public class BeanFactory {

    private Map<String, BeanConfig> beanDefinitionMap = new HashMap<>();

    private BeanRegister beanRegister;

    public BeanFactory() {
        //创建bean注册器
        beanRegister = new BeanRegister();
        //加载资源
        this.beanDefinitionMap = new ResourceLoader().getResource();
    }

    /**
     * 获取bean
     *
     * @param beanName bean名称
     * @return
     */
    public Object getBean(String beanName) {
        //从bean缓存中取
        Object bean = beanRegister.getSingletonBean(beanName);
        if (bean != null) {
            return bean;
        }
        //根据bean定义,创建bean
        return createBean(beanDefinitionMap.get(beanName));
    }

    /**
     * 创建Bean
     *
     * @param beanConfig bean定义
     * @return
     */
    private Object createBean(BeanConfig beanConfig) {
        try {
            Object bean = beanConfig.getBeanClass().newInstance();
            //缓存bean
            beanRegister.registerSingletonBean(beanConfig.getBeanName(), bean);
            return bean;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

生产销售:测试

  • UserDao.java

    我们的Bean类,很简单

/**
 * 生产销售:测试
 */
public class UserDao {
    public void queryUserInfo(){
        System.out.println("new A good man.");
    }
}

单元测试:

import org.junit.Test;

public class ApiTest {

    /**
     * 演示一个简单的spring容器框架
     */
    @Test
    public void test_BeanFactory() {

        //1.创建bean工厂(同时完成了加载资源、创建注册单例bean注册器的操作)
        BeanFactory beanFactory = new BeanFactory();

        //2.第一次获取bean(通过反射创建bean,缓存bean)
        UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
        userDao1.queryUserInfo();

        //3.第二次获取bean(从缓存中获取bean)
        UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");
        userDao2.queryUserInfo();
    }
}

结果:

 

posted @ 2022-04-02 16:34  威兰达  阅读(47)  评论(0编辑  收藏  举报