掘金 后端 ( ) • 2024-03-27 09:52

一、前瞻

经过上篇源码阅读博客的实践,发现按模块阅读也能获得不少收获,而且能更加系统地阅读源码。

今天的阅读方式还是按模块阅读的方式,以下是Spring各个模块的组成。

在这里插入图片描述

那今天就挑Beans这个模块来阅读,先思考下本次阅读的阅读线索

  1. Beans模块使用了什么设计模式
  2. Beans模块里的Bean采用什么数据结构进行存储
  3. Beans模块里的Bean被Spring IOC容器管理,那管理Bean的具体实例是谁

二、探索

Ok,先整体看下Beans模块的代码组织结构。

在这里插入图片描述

看了组织结构,很好奇为什么spring不多创建几个文件包来分类,这个先作为我们的阅读线索4,等下继续探索。

factory包很显眼在第一个,应该就是创建Bean的工厂,而且应该使用了不少设计模式,我们按这个为入口进行探索。

在这里插入图片描述

我们以BeanFactory接口的实现类ClassPathXmlApplicationContext,来看看factory这个工厂是如何运行的,顺便解决我们的阅读线索1、2。

以下是ClassPathXmlApplicationContext的类图。

在这里插入图片描述

从类图里的链条可以知道,BeanFactory接口通过一系列的链条,生成了最终的实例ClassPathXmlApplicationContext。

BeanFactory作用在于生产Bean对象,而子类实现ClassPathXmlApplicationContext通过Class Path这种方式来生产Bean对象。

也就是采用了工厂方法模式,使每一个不同的子类实现都封装成了一个对象。

到这我们解决了阅读线索1

Beans模块使用了什么设计模式

我们再看看阅读线索2:Beans模块里的Bean采用什么数据结构进行存储

public interface BeanFactory {

	/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * <p>This method allows a Spring BeanFactory to be used as a replacement for the
	 * Singleton or Prototype design pattern. Callers may retain references to
	 * returned objects in the case of Singleton beans.
	 * <p>Translates aliases back to the corresponding canonical bean name.
	 * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
	 * @param name the name of the bean to retrieve
	 * @return an instance of the bean.
	 * Note that the return value will never be {@code null} but possibly a stub for
	 * {@code null} returned from a factory method, to be checked via {@code equals(null)}.
	 * Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
	 * @throws NoSuchBeanDefinitionException if there is no bean with the specified name
	 * @throws BeansException if the bean could not be obtained
	 */
	Object getBean(String name) throws BeansException;
}

BeanFactory主要是提供获取Bean的功能,那存储Bean的应该就在类图里链条的中间。

在这里插入图片描述

我们在链条的中间AbstractApplicationContext找到了getBen方法的实现,既然可以get,那存储Bean的数据结构也应该在里面。

@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
    DefaultListableBeanFactory beanFactory = this.beanFactory;
    if (beanFactory == null) {
        throw new IllegalStateException("BeanFactory not initialized or already closed - " +
                                        "call 'refresh' before accessing beans via the ApplicationContext");
    }
    return beanFactory;
}

最终定位到了这段代码,可以看到引用了DefaultListableBeanFactory类,我们打开这个类。

在这里插入图片描述

可以看到存储Bean的最终数据结构就是这些Map,还采用了ConcurrentHashMap来支持并发,而Map的Key BeanDefinition就是Bean本身

到这我们就解决了阅读线索2

Beans模块里的Bean采用什么数据结构进行存储

阅读线索3也显而易见,管理Bean的也就是存储Bean的这些对象,如上文的DefaultListableBeanFactory

Beans模块里的Bean被Spring IOC容器管理,那管理Bean的具体实例是谁

我们可以全局搜索下private final Map<String, BeanDefinition>,看看哪些对象在存储Bean对象。

在这里插入图片描述

可以看到有两个,其中一个便是便是我们上文所探索的DefaultListableBeanFactory

三、总结

我们再来看看中间新加入的阅读线索4,不知大家忘记了没。

我们可以对照图1的代码组织结构,发现这些没存储在包里的功能类都是比较杂乱的,想必是Spring觉得目前这些功能类还构不成一个包的体系,可能后面规模更大会统一集成起来管理。

未完待续。。。

好了,今天的分享就到这👀。大家能否感受到通过按模块这种方式来阅读源码的乐趣呢

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️