程序员社区

Spring 框架源码解读1


title: Spring 框架源码解读1
date: 2020/04/14


前言

为什么要阅读 Spring 源码?

作为一个 Java 后台开发,在工作中肯定离不开 Spring,但是却对 Spring 中的实现原理只有大致的了解,没有深入的理解,所以想要通过这部分专栏带大家一起“深入”的了解 Spring 源码。

Spring 简介

Spring 是一个 IoC(Inversion of Control,控制反转)框架。

IoC 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递(注入)给它。 —— 维基百科

为什么需要 IoC ?

Java 是面向对象语言,在 Java 中万物皆对象,我们的程序就是由各种对象组成的,当多个类之间关系变成下面这样复杂的时候,维护起来心里可能会有1万只“草泥马”。

Spring 框架源码解读1插图

我们知道,在计算机领域,没有什么问题是加一层解决不了的,所以为了解决这个问题,我们引入 IoC 框架,让框架来维护类与类之间那错综复杂的关系,使我们解脱出来。

Spring 框架源码解读1插图1

这个时候我们发现,我们类之间的关系都由 IoC 框架负责维护类,同时将类注入到需要的类中。

类的使用者只负责使用,而不负责维护。

本专栏内容

1、我会带着大家从0开始自己写一个 IoC 框架,功能与 Spring 大体相同。

2、当实现完一部分功能之后,我就会带着大家一起看看 Spring 0.9 中是怎样实现的

3、然后再分析与 5.0 版本的区别

本节内容 & 思考题

今天会带大家动手搓一个 BeanFactory。

大家想一下在 Spring 中 BeanFactory 的作用是什么,它采用了什么设计模式?

手写 BeanFactory

1、创建 Maven 项目,引入 hutool 工具类

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>4.6.1</version>
</dependency>

2、书写 BeanDefinition

BeanDefinition 定义了 bean 的信息,例如:它的名字、全类名、单例还是原型等

/**
 * bean 的属性
 *
 * @author yujx
 * @date 2020/04/14 14:57
 */
public interface BeanDefinition {

    /**
     * 名称
     */
    String getName();

    /**
     * 设置名称
     */
    void setName(String name);

    /**
     * 类名称
     */
    String getClassName();

    /**
     * 设置类名称
     */
    void setClassName(String className);

    ScopeEnum getScope();

    void setScope(ScopeEnum scope);


    enum ScopeEnum {
        SINGLETON,
        PROTOTYPE
    }
}

/**
 * 默认实现(Spring 中有两个实现 RootBD 和 ChildBD 其中 ChildBD 我们基本用不到)
 *
 * @author yujx
 * @date 2020/04/14 15:05
 */
public class DefaultBeanDefinition implements BeanDefinition {

    // 名称
    private String name;

    // 全类名
    private String className;

    // 生命周期
    private ScopeEnum scope = ScopeEnum.SINGLETON;

    /**
     * 名称
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * 设置名称
     */
    @Override
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 类名称
     */
    @Override
    public String getClassName() {
        return className;
    }

    /**
     * 设置类名称
     */
    @Override
    public void setClassName(String className) {
        this.className = className;
    }

    @Override
    public ScopeEnum getScope() {
        return scope;
    }

    @Override
    public void setScope(ScopeEnum scope) {
        this.scope = scope;
    }
}

3、定义 BeanFactory 接口

BeanFactory 是生产 bean 的工厂

AbstractBeanFactory 实现了 BeanFactory 中的所有方法,并维护了单例的 Map,并提供一个抽象方法getBeanDefinition(beanName)

在 Spring 中 ListableBeanFactory 接口继承了它并扩展了它,提供了遍历 bd 相关的方法

ListableBeanFactoryImpl 继承了 AbstractBeanFactory 并实现了 ListableBeanFactory 接口,实现了他们的抽象方法,它的主要功能是维护 bd 的 Map。

JsonBeanFactory 是 Spring 中 XmlBF 的代替品,因为我不想解析恶心人的 xml,它负责读取 json 文件,向 bf 中注册 bd 信息。

/**
 * 生产 bean 的工厂
 *
 * @author yujx
 * @date 2020/04/14 14:37
 */
public interface BeanFactory {

    /**
     * 根据名称获取对应的 bean(工厂方法模式)
     */
    Object getBean(String name);

    /**
     * 根据名称和它的类型获取对应的 bean
     */
    <T> T getBean(String name, Class<T> requiredType);
}

/**
 * 负责维护单例对象(在 Spring 源码中该类还负责维护 parent 的bf,应该是为了 Spring MVC)
 *
 * @author yujx
 * @date 2020/04/14 19:14
 */
public abstract class AbstractBeanFactory implements BeanFactory {

    // key:名字 value:单例对象
    private final Map<String, Object> sharedInstanceCache = new ConcurrentHashMap<>();

    /**
     * 根据名称获取对应的 bean (工厂方法模式)
     */
    @Override
    public Object getBean(String name) {
        if (sharedInstanceCache.containsKey(name)) {
            return sharedInstanceCache.get(name);
        }

        // 获取 bean 的属性信息
        BeanDefinition beanDefinition = this.getBeanDefinition(name);
        if (ObjectUtil.isNull(beanDefinition)) {
            throw new RuntimeException("获取的bean不存在!");
        }

        Object bean = this.createBean(beanDefinition);
        // 如果该对象是单例的,则加入到缓存中。
        if (beanDefinition.getScope().equals(BeanDefinition.ScopeEnum.SINGLETON)) {
            sharedInstanceCache.put(name, bean);
        }
        return bean;
    }

    // 根据 bd 创建对象
    private Object createBean(BeanDefinition beanDefinition) {
        return ReflectUtil.newInstance(beanDefinition.getClassName());
    }


    /**
     * 根据名称和它的类型获取对应的 bean
     */
    public <T> T getBean(String name, Class<T> requiredType) {
        Object bean = getBean(name);
        if (bean.getClass().equals(requiredType)) {
            return (T) bean;
        }
        throw new RuntimeException("获取bean的类型错误!");
    }

    /**
     * 根据名称获取 bd (子类实现)
     *
     * @param beanName
     * @return
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName);
}


/**
 * 扩展了bf,可以根据类型返回容器中所有的 bean
 * <p>
 * BeanFactory的扩展将由可以枚举其所有bean实例的bean工厂来实现,而不是按照客户的要求按名称一一尝试。
 *
 * @author yujx
 * @date 2020/04/14 14:48
 */
public interface ListableBeanFactory extends BeanFactory {

    /**
     * 根据类型获取容器中所有该类型的对象
     */
    <T> Map<String, T> getBeansOfType(Class<T> type);
}


/**
 * ListableBeanFactory 工厂的实现,并负责维护 bd 信息
 *
 * @author yujx
 * @date 2020/04/14 14:56
 */
public class ListableBeanFactoryImpl extends AbstractBeanFactory implements ListableBeanFactory {

    // key:名字 value:bean 的信息
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    // key:字节码类型 value:名字数组(Spring 源码中是通过 beanDefinitionMap 遍历出来的,我们这里为了方便)
    private final Map<Class<?>, Set<String>> allBeanNamesByType = new ConcurrentHashMap<>(64);

    /**
     * 注册 bean 的信息
     */
    protected final void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(name, beanDefinition);

        // 将相同类型的beanName放入 allBeanNamesByType 中
        String className = beanDefinition.getClassName();
        Class<?> type = this.getType(className);

        Set<String> beanNameSet = allBeanNamesByType.getOrDefault(type, new HashSet<>(1));
        beanNameSet.add(beanDefinition.getName());
        allBeanNamesByType.put(type, beanNameSet);
    }

    // 根据全类名获取类型
    private Class<?> getType(String className) {
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据类型获取容器中所有该类型的对象
     */
    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) {
        Set<String> beanNameSet = allBeanNamesByType.get(type);
        if (ObjectUtil.isNull(beanNameSet)) {
            return new HashMap<>(0);
        }

        Map<String, T> map = new HashMap<>(beanNameSet.size());
        for (String beanName : beanNameSet) {
            map.put(beanName, this.getBean(beanName, type));
        }
        return map;
    }

    /**
     * 根据名称获取 bd (子类实现)
     *
     * @param beanName
     * @return
     */
    @Override
    protected BeanDefinition getBeanDefinition(String beanName) {
        return beanDefinitionMap.get(beanName);
    }
}


/**
 * 因为我不想解析xml,所以用json来代替
 * <p>
 * 负责从不同类型文件中读取配置并注册进 bd 的 Map 中
 *
 * @author yujx
 * @date 2020/04/14 15:26
 */
public class JsonBeanFactoryImpl extends ListableBeanFactoryImpl {

    public JsonBeanFactoryImpl(String fileName) {
        JSONArray jsonArray = JSONUtil.readJSONArray(new File(fileName), StandardCharsets.UTF_8);
        this.loadBeanDefinitions(jsonArray);
    }

    private void loadBeanDefinitions(JSONArray jsonArray) {
        List<DefaultBeanDefinition> beanDefinitionList = jsonArray.toList(DefaultBeanDefinition.class);
        for (DefaultBeanDefinition bd : beanDefinitionList) {
            super.registerBeanDefinition(bd.getName(), bd);
        }
    }
}

4、测试

public class Apple {

    private String name = "红富士";

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Apple{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class TestJsonBF {
    public static void main(String[] args) {
        ListableBeanFactory bf = new JsonBeanFactory("/Users/x5456/IdeaProjects/Summer/src/test/resources/apple.json");
        Apple apple = bf.getBean("apple", Apple.class);
        System.out.println(apple);

        Map<String, Apple> beansOfType = bf.getBeansOfType(Apple.class);
        System.out.println(beansOfType);
    }
}

apple.json

[
{"name":"apple","className":"cn.x5456.summer.Apple"},
{"name":"apple2","className":"cn.x5456.summer.Apple"},
{"name":"apple3","className":"cn.x5456.summer.Apple"}
]

其实我觉得 BeanFactory、AbstractBeanFactory、ListableBeanFactoryImpl、JsonBeanFactoryImpl 他们几个符合单一职责,他们虽然是父子关系,但他们几个彼此之间都在做不同的事情。

脑海中有一个念头告诉我,这应该是不好的,应该可以用设计模式优化,但是我想不起来。

Spring 0.9 中的实现

BeanFactory 和我的写法基本相同,相较于多了别名相关的东西。

AbstractBeanFactory 中的方法也基本相同,多了一个父BF。

Spring 框架源码解读1插图2

ListableBeanFactoryImpl 也和我的写法相同

XmlBeanFactory 也基本相同

Spring 框架源码解读1插图3

Spring 5.0 中区别

各个类的职责依然没变,只是方法增多了很多,依赖注入部分增加了对注解的支持(AbstractAutowireCapableBeanFactory),虽然依赖注入我还没说,XmlBF 也已经被标注成废弃,现在主要使用的是 ApplicationContext 了,ApplicationContext 部分我们下次再说。

思考题答案

BeanFactory 是用来获取我们交给 Spring 容器管理的对象的。

它采用了工厂方法模式。

赞(0) 打赏
未经允许不得转载:IDEA激活码 » Spring 框架源码解读1

一个分享Java & Python知识的社区