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万只“草泥马”。

image

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

image

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

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

本专栏内容

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

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

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

本节内容 & 思考题

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

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

手写 BeanFactory

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


    cn.hutool
    hutool-all
    4.6.1

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 getBean(String name, Class 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 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 getBean(String name, Class 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
 * 

* BeanFactory的扩展将由可以枚举其所有bean实例的bean工厂来实现,而不是按照客户的要求按名称一一尝试。 * * @author yujx * @date 2020/04/14 14:48 */ public interface ListableBeanFactory extends BeanFactory { /** * 根据类型获取容器中所有该类型的对象 */ Map getBeansOfType(Class type); } /** * ListableBeanFactory 工厂的实现,并负责维护 bd 信息 * * @author yujx * @date 2020/04/14 14:56 */ public class ListableBeanFactoryImpl extends AbstractBeanFactory implements ListableBeanFactory { // key:名字 value:bean 的信息 private final Map beanDefinitionMap = new ConcurrentHashMap<>(); // key:字节码类型 value:名字数组(Spring 源码中是通过 beanDefinitionMap 遍历出来的,我们这里为了方便) private final Map, Set> 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 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 Map getBeansOfType(Class type) { Set beanNameSet = allBeanNamesByType.get(type); if (ObjectUtil.isNull(beanNameSet)) { return new HashMap<>(0); } Map 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来代替 *

* 负责从不同类型文件中读取配置并注册进 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 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 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。

image

ListableBeanFactoryImpl 也和我的写法相同

XmlBeanFactory 也基本相同

image

Spring 5.0 中区别

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

思考题答案

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

它采用了工厂方法模式。

你可能感兴趣的:(Spring 框架源码解读1)