那些你应该掌握的 Spring 原理

参考文章

《Spring In Action, 2nd.Ed.》
Difference between beanfactory and-applicationcont
Spring Framework Annotations
The IoC container
BeanFactoryPostProcessor and BeanPostProcessor in lifecycle events

前言

spring 目前所代表的已经不是一个框架, 或者多个框架, 而是 java 企业级应用中一整套体系化的解决方案。
这一点从 spring 官方的宣传图上就可以看出来。
在这里插入图片描述
对于普通 java 开发者而言, 应用 spring 已经是再熟悉不过的事情了, 本文旨在梳理一些 spring 应用级别之外, 应该被进阶开发者所掌握的原理性知识。

由于 spring 的内容已经涵盖了方方面面, 本文仅会涉及 spring 中最核心, 最值得普通开发者掌握的一些原理性知识。

Spring 的基本思想:控制反转/依赖注入(IoC/DI)

首先明确一下, 控制反转(IoC, Inversion of Control) 和 依赖注入(DI, Dependency Injection) 是一个概念的两种称呼, 新手同学不必纠结, 后文可能会经常交叉使用这两个不同称呼。

在展开学习 SpringFramework 的实现原理之前,值得思考的是, 所谓的依赖注入(DI)到底有什么优点, 以致于被如此广泛的使用。

以下这段话引自 《Spring In Action, 2nd.Ed.》作者 Craig Walls , 他同样是 Pivotal 公司的开发人员( 不了解 Pivotal 公司的童鞋需要知道: Spring 以及衍生框架、Redis 存储、消息队列框架 RabbitMQ 等都出自 Pivotal 公司)

“Any nontrivial application is made up of two or more classes that collaborate with each other to perform some business logic. Traditionally, each object is responsible for obtaining its own references to the objects it collaborates with (its dependencies). When applying DI, the objects are given their dependencies at creation time by some external entity that coordinates each object in the system. In other words, dependencies are injected into objects.”

任何正式的应用都是由多个类相互协作, 各自实现不同的业务逻辑完成的。 传统上,每一个对象都是自行负责获取它所依赖对象引用的。 但是应用了 DI(Dependency Injection)后, 每个对象所依赖的对象不再由其自身负责获取, 而是统一由一个外部实体在创建这些需要相互协作的对象时提供。

上面这段话, 有些抽象, 通过代码和图片更加形象地表述一下

应用 控制翻转 / 依赖注入 (IoC/DI)之前

public class A {
    public void doSomethingByA(){
        // 实现一些 A 逻辑

        // 需要 B 类执行一些 B 逻辑
        // new B 也可以换做是 ActionFacotry.createB() 之类的工厂方法
        // 这里的关键是, 由 A 关心怎么获得 b 的引用
        B b = new B();

        // 执行 B 逻辑
        b.doSomethingB();
    }
}

由于在类 A 中直接编写了构造 B(或者是获取 B 的应用的代码), A 与 B 类变成了一种紧耦合的协作方式, 下图可以形象的表达这种耦合关系。
在这里插入图片描述
这种耦合意味着, 如果我们希望更换 A 类获取的类 B 引用, 不得不更改 A 类的代码 ( 图中可以形象理解为, 要把卡住红色部分的蓝色部分砸开 )。

有同学可能会在这里指出, 如果使用工厂模式 BFacotry 用于创建类型为 B 的实例, 那么不需要修改类 A 也可以更换类 B 的引用。

但实际上, 这并没有太大区别, 只是把 A与 B 的耦合替换成了 A 与 BFactory 的耦合而已, 会进一步引入 BFactory 的引用如何获取的问题

应用 控制翻转 / 依赖注入 (IoC/DI)之前, 代码是这项协作的 。

应用 控制翻转 / 依赖注入 (IoC/DI)之后

public class A {
	@Autowired
	private B b;
    public void doSomethingByA(){
        // 实现一些 A 逻辑
        
        // 执行 B 逻辑
        b.doSomethingB();
    }
}

由于 A 类的代码中没有编写任何获取 B 类引用的代码, 使得 A 与 B 的耦合关系变成了如下的松耦合状态
在这里插入图片描述

这样当我们想要替换 B 的引用时, 我们就完全无需改动 A 的代码。

松耦合的好处

这个地方可能还会有同学进一步产生疑问, 为什么总是要假设改变 B 的引用, 实际开发中,相互依赖的类在很多情况下, 并不需要动态修改啊。

这里的答案是 测试需求

想象一下, 你要编写一段针对某个类某个方法进行单元测试, 由于这个方法的正常执行会依赖很多其他的类, 于是在构造测试环境时, 需要 mock 很多用于测试的假的对象。

如果你编写的代码使用了 IoC 容器, 想要为待测试的组件提供一个虚假的 mock 引用就变成了很容易的事。 反之, 如果没有使用 IoC 容器, 那么在不改动代码的情况下, 替换待测试类的依赖组件就变成了一个很难实现的目标。

除去测试需求, 即便你没有灵活替换某个类引用的需求, 将对象之间的引用关系维护放在一个统一的组件中管理, 也可以使得系统组件中错综复杂的关系变得有迹可循

IoC 就没有副作用吗

有, 就像前面提到的, 必要的耦合关系是不能被消除的, 只是被从一种耦合替换为另外一种耦合。使用 IoC 容器后, 由于将系统中组件依赖关系都交给了容器去维护, 整个系统就与容器耦合了起来。
在这里插入图片描述
也就是说, 如果你的系统有一天想要替换框架, 这就变得很困难, 当然这种需求很少会有, 所以 Spring IoC 框架已经近乎成为了 java 世界中企业应用的标准框架。

Spring 的核心: Bean 容器

根据 spring-framework 官方文档给出的定义, 由 Spring IoC 容器实例化, 组装, 管理的对象被统称为 Bean。 所以后文我们都会用 Bean 来描述被 Spring 所创建以及管理的对象。
在这里插入图片描述
Spring IoC 实现的最基本的功能是对象的的创建与注入, 所以从最原始的抽象层面考虑, 至少存在如下 3 个步骤。

  1. 获取 Bean 的定义信息

  2. 根据 Bean 的定义信息创建 Bean

  3. 在需要时刻, 将创建好的 Bean_A 注入 Bean_B

所以, 如果要理解 Spring, 应该从源码角度其理解 Spring 是如何实现这三个基本步骤的。

获取 Bean 的描述信息:BeanDefinition

Xml 形式的 Bean 描述

Spring 框架刚创建时, 是通过 xml 文件描述一个 Bean 的。例如下图
在这里插入图片描述

即描述了一个 class 为 MyTestBean 的对象, 其 id 被定义为 myTestBean

public class MyTestBean {
	private String testStr = "testStr";
	
	public String getTestStr(){
			return testStr;
	}
	public void setTestStr(String testStr){
			this.testStr = testStr;
	}
	
}

然后可以通过声明 XmlBeanFacotry 的方式来解析 xml, 加载对应的 bean

public class TestXmlBeanFactory{
	@Test
	public void testLoadBeanFromXml(String testStr){
		BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"))
		MyTestBean bean = (MyTestBean) bf.getBean("myTestBean");
		assertEquals("testStr",bean.getTestStr());
	}
	
}

Annotation 形式的 Bean 描述

自从 Java 5.0 开始提供 Annotation 的语法支持后, Spring Framework 从 Release 2.5 版本开始, 也支持了通过 Annotation 来定义 Bean。由于 Annotation 的便捷性, Xml 形式的 bean 配置文件越来越少, 以至当下最为流行的 Spring Boot 通常使用纯注解的方式完成 Bean 的描述

@Component
public class Customer {
    private Person person;
    @Autowired
    public Customer (Person person) {          
      this.person=person;
    }
}

@Component 注释的类, 只要在 @ComponentScan 所配置的扫描路径下, 就会被 Spring 自动加入 bean 的管理范围中。 被 @Autowired 修饰的域或构造器, 可以自动根据需要构造器的参数类型, 找到合适的 bean 予以注入。

两类 IoC 容器: BeanFactory vs ApplicationContext

对于 Bean 的管理, Spring 事实上提供了两类容器接口:

  1. BeanFactory
  2. ApplicationContext

这里引用一个 StackOverFlow 上发现的清晰的图, 可以较为清晰的体现两类容器接口的差别

Feature BeanFactory ApplicationContext
Annotation support No Yes
BeanPostProcessor Registration Manual Automatic
implementation XmlBeanFactory(Deprecated Since 3.1)
–> XmlBeanDeifinitionReader + DefaultListBeanFactory
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
WebXmlApplicationContext
internationalization No Yes
Enterprise services No Yes
ApplicationEvent publication No Yes

从上面的对比表格中, 我们可以发现, ApplicationContext 的功能比 BeanFactory 更为全面。 从接口关系上来说, 也确实是这样, ApplicationContext 继承了 BeanFactory( 注意这里是接口的继承)。

Tips: BeanFactory 和 ApplicationContext 的区分关键点并不在于是否支持 Annotation。
因为 BeanFactory 接口本身并没有定义任何与 Xml 或者 Annotation 有关的方法。
只是 Spring恰好没有提供一个只实现了 BeanFactory 接口的类去支持
Anotation 形式的 Bean 定义读取。

两类容器的接口关系及相关实现类的 UML 图如下(注意图中并未对 Interface 和 Class 进行图形上的区分标注)。
在这里插入图片描述上图的容器实现类主要区别如下:

  • XmlBeanFactory 从 Xml 配置文件中获取 Bean 的描述信息, 该类已经从 Spring 3.1 之后被标注为 @Deprecated , 相对应的, 鼓励使用 XmlBeanDefinitionReaderDefaultListableBeanfacotory 来完成原本 XmlBeanFactory 所实现的功能。
  • FileSystemXmlApplicationContext 从特定完整路径的 Xml 配置文件获取 Bean 描述信息
  • ClassPathXmlApplicationContext 从类路径中的Xml 配置文件获取 Bean 描述信息
  • AnnotationConfigApplicationContext 从注解类型的配置中获取 Bean 的描述信息
  • AnnotationConfigWebApplicationContextXmlWebApplicationContext 顾名思义, 分别从 Annotation 和 Xml 中获取 Bean 的描述信息, 另外, 这种容器专门用于为 Web 类型的应用提供 Bean 的管理

只是两种不同的描述格式

对于 Spring 容器来说, 无论是通过 xml 获取 Bean 的定义, 还是通过 annotation 获取 bean 的定义, 只是两种不同的信息获取渠道而已, 前者通过解析 xml 文件内容完成, 后者通过扫描 java class 文件, 利用 jvm 提供的反射功能, 获取 annotation 信息 。

Spring 具体解析 xml 或着 class 文件的过程不必细究, 需要了解的是,这些 Bean 的描述信息, 最终会被存储在一个 BeanDefination 的实例中, spring 容器后续会以此为基础来创建以及管理 Bean

BeanDefinition

正如前文所描述, 通过 xml 和 annotation 获取到 Bean 的描述信息后, 肯定需要将其统一存储和管理起来。 在 Spring 框架代码中, Bean 的描述信息的最终存储形式即为 BeanDefinition

BeanDefinition 是一个接口, 在 Spring 中有三种实现:

  1. RootBeanDefinition
  2. ChildBeanDefinition
  3. GenericBeanDefinition

这三种实现,均继承自 AbstractBeanDefinition。

在这里插入图片描述
BeanDefinition 接口中包含了所有 Bean 描述信息的获取方法, 例如:

  • getBeanClassName()
  • isLazyInit()
  • getScope()

其中, RootBeanDefinition 是最常用的实现类, 它对应于 Xml 文档中的 标签 或者 Annotation 中的 @Component, @Bean 等类型注解所描述的 Bean 。

ChildBeanDefinition 的出现来源于 Xml 文档中提供的 Bean 继承功能, 这项功能在 Annotation 中并没有对应实现, 原因是使用 Annotation 描述 Bean 时, java 语言本身就足以提供 xml 文档通过 Bean 继承实现的功能。

GenericBeanDefinition 则主要用于注册用户可见的 BeanDefinition, 例如, 用户会通过 post-processor 来修改 BeanDefinition 的属性。

BeanDefinition 的管理

明确了 BeanDefinition 的概念以后, 就可以思考 Spring 如何管理 BeanDefinition 了, 整体首先肯定要划分为如下步骤:

  1. 从 Xml 或 Annotation 中读取 Bean 的原始描述信息, 实例化为 BeanDefinition 接口实现类的实例
  2. 将众多 BeanDefinition 的信息统一存储到一个数据结构中, 最容易想到的是一个 Map 中
  3. 根据需求, 在特定的时刻获取 BeanDefinition , 完成 Bean 的创建与管理

BeanDefinition 的读取与注册

由于 Annotation 是现在最为广泛使用的 Bean 定义来源,以 Spring Boot 框架为例学习, BeanDefiniton 的读取与注册过程。

Spring Boot 典型的包含 main 方法的启动类代码如下:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}    

对于 SpringApplication 的 run 方法中的源码做删减之后, 可以得到如下的核心步骤

		// 注意下面代码在源码基础上删减了一些次要步骤, 例如 environment , applicatioNArguments 的的构建操作等
		ConfigurableApplicationContext context = null;
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
					
			context = createApplicationContext();
			
			refreshContext(context);
			
			afterRefresh(context, applicationArguments);
		}

上面的源码完成了

  • 应用启动时监听器 SpringApplicationRunListener 获取注册
  • ApplicationContext 的预处理( prepare)
  • ApplicationContext 的创建 ( create )
  • ApplicationContext 的刷新(refresh)

(拓展内容开始)

在执行 prepareContext 方法时, 会广播一个 ApplicationEnvironmentPrepared类型的 event, 触发监听了该事件类型的 ApplicationListener 的 onApplicationEvent(ApplicationEnvironmentPreparedEvent event) 方法执行。 如果使用了 SpringCloud, 此时会触发BootstrapApplicationListener 的 onApplicationEvent 方法, 其中包含一段如下代码

		ConfigurableApplicationContext context = null;
		... 其他代码...
		if (context == null) {
			context = bootstrapServiceContext(environment, event.getSpringApplication(),
					configName);
		}
		... 其他代码...

这里的 bootstrapServiceContext 方法内部还会再次调用 SpringApplication.run() 的方法, 创建一个 contextId 为 “bootstrap” 的 context, 该 context 会成为主应用 ApplicationContext 的 parent 。

更详细的信息可以参见: Spring Cloud Context: Application Context Services, 这里为保持文章主线清晰, 不做展开。

拓展内容结束


回到 SpringApplication.run() 的主流程中, 如果启动的是一个典型的 web 应用, 那么 createApplicationContext() 会返回一个 AnnotationConfigServletWebServerApplicationContext 的实例。

在这里观察下 ApplicationContext 中 beanFacotory 中的变量 beanDefinitionMap
在这里插入图片描述
可以看到, 在刚刚执行完 createApplicationContext() 方法后, 获得的 context 实例中的 beanDefinitionMap 变量中已经产生了 6 个键值对。

这说明在这个步骤中, 已经发生了 beanDefinition 的获取。 于是查看一下 createApplicationContext 的内容

... 其他代码 ...
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

... 其他代码 ...
protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			... 其他代码 ...
		}
		// 下面是实例化操作
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

可以看到, createApplicationContext() 方法就是简单的根据应用类型, 实例化了特定的 ApplicationContext 类, 在笔者测试的样例中, 由于测试的是 Servlet 类型的应用, 这里实例化的类就是 AnnotationConfigServletWebServerApplicationContext

进一步查看 BeanUtils.instantiateClass(contextClass) 的方法, 可以看到实例化操作是通过调用 context 类的无参数构造器(constructor) 完成的。

public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
		... 其他代码 ...
		try {
			Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ?
					KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
			return instantiateClass(ctor);
		}
		... 其他代码 ...
	}

进一步查看 AnnotationConfigServletWebServerApplicationContext 无参构造函数内容

public AnnotationConfigServletWebServerApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

构造函数初始化了 AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner 两个变量, 从字面上可以看出来, 这两个类分别负责了 BeanDeifinition 的读取和扫描。

当然, 到这里为止, 依然没有发生任何 beanDefinition 的获取, 我们需要继续追踪, 先前的 6 个 beanDefinition 是在哪里获取的。

步进到 AnnotatedBeanDefinitionReader 的构造函数中

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

注意到一个可疑关键步骤 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

这一步骤按照方法名的字面含义, 注册了注解形式配置(AnnotationConfig)的处理器。

步进到该函数中可以看到如下形式的代码

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		
		... 其他代码 ...
		
		// 下面的这段代码直接根据固定的类, 构造了 RootBeanDefinition, 添加了到了 registry 中, 刚刚提到的 6 个beanDefinition 都是以下面这种方式完成注册的
		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
		
		... 其他代码(省略了 5 个和上面类似的注册步骤) ...

		return beanDefs;
	}
	
... 其他代码 ...
private static BeanDefinitionHolder registerPostProcessor(
			BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {

		definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		// 下面这个操作最终会触发 beanDefinitionMap.put(beanName, beanDefinition);
		registry.registerBeanDefinition(beanName, definition);
		return new BeanDefinitionHolder(definition, beanName);
	}	

小结一下, 至此我们已经搞清楚, 在 SpringApplication.run 方法中的 createApplicationContext 执行后, 最先被放置到 context 中的 6 个 beanDefinition 的来源。同时也发现了 AnnotationConfigServletWebServerApplicationContext 在构造的时候, 会分别初始化一个 AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner

public ConfigurableApplicationContext run(String... args) {

		... 其他代码 ...
		
		try {
			context = createApplicationContext();
			// 在上面这一步骤执行完后, 已经有 6 个最为基础的 beanDeifinition 被默认加载到 context 中: 
			/* 1. internalConfigurationAnnotationProcessor
					--> 对应类 ConfigurationClassPostProcessor 
			   2. internalAutowiredAnnotationProcessor
					--> 对应类 AutowiredAnnotationBeanPostProcessor
			   3. internalRequiredAnnotationProcessor
			   		--> 对应类 RequiredAnnotationBeanPostProcessor
			   4. internalCommonAnnotationProcessor
			   		--> 对应类 CommonAnnotationBeanPostProcessor
			   5. internalEventListenerProcessor
			   		--> 对应类 EventListenerMethodProcessor
			   6. internalEventListenerFactory
			   		--> 对应类 DefaultEventListenerFactory
			 */
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);		
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			
		... 其他代码 ...
	
		}
		return context;
	}

可以注意到被默认预先加载的 6 个 beanDefinition 中, 前 4 个基本都是某一种 AnnotationProcessor 。 从类名上我们可以预判, 这些 beanDefinition 得到实例化之后, 会被用于处理各类注解。 我们需要进一步探索, 各种注解的读取时机, 以及这些 Processor 的调用时机。

继续顺着 run 方法阅读源码, createApplicationContext 后的下一个关键步骤是 refreshContext(context) , 顺着该方法可以跟进到 AbstractApplicationContext 中的 refresh 方法, 看到如下代码

			// (1) Prepare this context for refreshing.
			prepareRefresh();

			// (2) Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// (3)Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// (4)Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// (5)Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// (6)Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// (7)Initialize message source for this context.
				initMessageSource();

				// (8)Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// (9)Initialize other special beans in specific context subclasses.
				onRefresh();

				// (10)Check for listener beans and register them.
				registerListeners();

				// (11)Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// (12)Last step: publish corresponding event.
				finishRefresh();
			}

从上述代码可以看到, refresh context 的过程被清晰地拆解为 12 个步骤, 每个步骤都配了清晰的注释。根据注释, 我们初步可以得到如下重要信息

  • 步骤 (2) 获取了一个 beanFactory , 默认情况会返回的实例类型是 DefaultListableBeanFactory
  • 步骤 (11) 通过获取到的 beanFactory , 实例化所有非延迟加载的 bean 到 context 中

注意, DefaultListableBeanFactory 是Spring 注册、加载 Bean 的默认核心实现类。


(DefaultListableBeanFactory 的一些细节介绍, 可以粗略阅读,熟悉一些接口名和类名)
在这里插入图片描述
从上面这张图可以清晰地看到, DefaultListableBeanFacotry 是如何通过一层层的继承, 来实现众多接口类的。 DefaultListableBeanFacotry 几乎实现了所有 Bean 管理所需要的接口, 所以后续 Spring 容器的原理多半都需要围绕其展开。

DefaultListableBeanFactory 的接口功能简要概括如下:

  • AliasRegistry
    • 定义对 Bean 别名的增删改等操作
  • BeanDefinitionRegistry
    • 定义对 BeanDefinition 的增删改操作
  • SingletonBeanRegistry
    • 定义对单例 Bean 的注册获取操作
  • BeanFacotry
    • 主要定义了对 Bean 以及 Bean 各种属性的获取操作
  • ListableBeanFactory
    • 继承了 BeanFactory , 在其基础上增加了批量获取 多个bean 的方法
      • 例如: <T> Map<String, T> getBeansOfType(@Nullable Class<T> type)
  • HierarchicalBeanFactory
    • 继承了 BeanFactory, 在其基础上, 增加了 getParentBeanFactory() 这个方法, 实现了该接口的 BeanFacotry 就就拥有了层级关系
  • ConfigurableBeanFactory
    • 继承了 HierarchicalBeanFactory, 提供 BeanFacotry 的多种 set 方法, 用于配置 BeanFacotroy, 其中有一个方法和 HierarchicalBeanFactory有关
    • void setParentBeanFactory(BeanFactory parentBeanFactory)
  • AutowireCapableBeanFactory
    • 顾名思义, 提供了非常核心的 Bean 创建以及 autowire 的功能,将 bean_A 自动注入到 bean_B 中
  • ConfigurableListableBeanFactory
    • 同时继承了 ConfigurableBeanFactory, ListableBeanFactory, AuwowireCapableBeanFacotry
    • 增加了 ignoreDependencyForType(Class<?> type) 方法, 主要用于, 在自动注入时, 忽略特定的类型属性注入
    • 增加了 void ignoreDependencyInterface(Class<?> ifc) 方法, 主要用于, 在自动注入时, 忽略特定的接口类型属性注入
    • 上面两种 ignore 方法的应用场景比较小众, 暂时不作深入

(DefaultListableBeanFactory 的细节介绍结束)


回到主流程中, 在 AbstractApplicationContext.refresh() 方法的 12 步骤中, 有趣的是我们好像并不能看出来, 大量的 beanDefinition 是哪一步读取解析的, 这需要进一步探索。

通过打断点观察可以发现,进入 refresh 方法时, context 中的 beanFactory 内变量 beanDeifinitionMap 包含 10 个元素(有 4 个是在 prepareContext 加载的)。 逐行执行后可以发现, 调用 invokeBeanFactoryPostProcessors(beanFactory); 后 ,beanDefinitionMap 中元素的数量明显增多, 在笔者的测试样例中, beanDeifinitionMap 中的元素增加到了 20 个, 说明在 invokeBeanFactoryPostProcessors 这一步骤里, 涉及到 beanDefinition 的获取。

	/**
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before singleton instantiation.
	 */
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	... 其他代码 ...
	}

从该方法的注释就可以看到, 该方法负责实例化调用之前注册好的 BeanFacotryPostProcessor 类型的 bean 。 这里回顾一下之前在 createApplicationContext 中预先加载的 6 个 beanDeifinition, 看看这些 bean 中有没有实现 BeanFactoryPostProcessor 接口。

挨个查看后, 可以得到如下信息

  • bean 名称: internalConfigurationAnnotationProcessor
    • 对应类: ConfigurationClassPostProcessor
    • 实现接口: BeanFacotryPostProcessor
  • bean 名称: internalAutowiredAnnotationProcessor
    • 对应类: AutowiredAnnotationBeanPostProcessor
    • 实现接口: BeanPostProcessor
  • bean 名称: internalRequiredAnnotationProcessor
    • 对应类 RequiredAnnotationBeanPostProcessor
    • 实现接口: BeanPostProcessor
  • bean 名称: internalCommonAnnotationProcessor
    • 对应类 CommonAnnotationBeanPostProcessor
    • 实现接口: BeanPostProcessor
  • bean 名称: internalEventListenerProcessor
    • 对应类 EventListenerMethodProcessor
    • 实现接口: SmartInitializingSingleton
  • bean 名称: internalEventListenerFactory
    • 对应类 DefaultEventListenerFactory
    • 实现接口: EventListenerFactory

(补充知识点开始)

BeanFacotryPostProcessor 和 BeanPostProcessor 的区别, 有必要在文章进一步深入之前明确一下,下图是引自 StackOverflow 一个回答。
在这里插入图片描述
可以注意到区别如下:

  • BeanFacotryPostProcessor 的调用时机是所有的 BeanDefinition 都被加载到容器后,此时还没有任何一个 Bean 被实例化。 BeanFacotryPostProcessor 可以用于修改 BeanDefinition 。
  • BeanPostProcessor 的调用时机是 Bean 被实例化之后, 它可以用于修改已经实例化后的 Bean

(补充知识结束)


注意到 internalConfigurationAnnotationProcessor 这个 bean 实现了 BeanFacotryPostProcessor , 可以在 invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) 中得到调用

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	
	PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	... 其他代码略 ...
	}

invokeBeanFactoryPostProcessors 的函数内容很长, 这里不作展开, 如果感兴趣自行阅读一下可以发现核心内容就分两个阶段, 区分优先级顺序分别调用 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 而已。

  • 阶段一: 调用 BeanDefinitionRegistryPostProcessor
    • BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor 接口,由于它会在其他 BeanFacotryPostProcessor 执行前先被调用, 可以用于在 BeanDeifinition Registry ( Bean 定义的注册表) 中进一步添加一些 beanDefinition 。
    • BeanDefinitionRegistryPostProcessor 的执行顺序分优先级, 实现了 PriorityOrdered 接口的先被执行。 接着是实现了 Ordered 接口, 最后是普通的
  • 阶段二: 调用 BeanFacotoryPostProcessor
    • BeanFactoryPostProcessor 的执行顺序也分优先级, 实现了 PriorityOrdered 接口的先被执行。 接着是实现了 Ordered 接口, 最后是普通的

先前注册的 ConfigurationClassPostProcessor 就实现了 BeanDefinitionRegistryPostProcessor 和 PriorityOrdered 接口, 可以得到优先地调用, 触发该类的 postProcessBeanDefinitionRegistry 方法执行

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		... 其他代码 ...
		this.registriesPostProcessed.add(registryId);
		processConfigBeanDefinitions(registry);
	}

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
			return;
		}

		// Sort by previously determined @Order value, if applicable
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// Detect any custom bean name generation strategy supplied through the enclosing application context
		SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

		if (this.environment == null) {
			this.environment = new StandardEnvironment();
		}

		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}	

for (String beanName : candidateNames) 这一行打断点查看一下
在这里插入图片描述
可以看到 candidateNames 的变量中有一个 BeanDefiniton 的名称为 apiApplication , 在笔者的项目中, 这个名称就是 SpringBoot 应用的 main 方法入口类, 这个 BeanDefinition 是在 prepareContext 步骤中添加到容器中的。 此入口类上有 SpringBoot 应用的核心注释 @SpringBootApplication

@SpringBootApplication(exclude= {UserDetailsServiceAutoConfiguration.class}
public class ApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }
}

查看一下@SpringBootApplicaion 这一注解的内容, 可看到如下内容
在这里插入图片描述
进一步查看 @SpringBootApplicaion 注解内容
在这里插入图片描述
发现 @SpringBootApplicaion 这一注解的效果之一就等同于添加了 @Configuration

所以添加了 @SpringBootApplicaion 注解的 main 方法主类, 会被添加到 configCandidates 变量中, 最终通过 parser.pase(candidates) 得到解析。

查看一下 ConfigurationClassParser 这个类的注释解析
在这里插入图片描述
可以看到, ConfigurationClassParse 的作用是解析被 @Configuration 修饰的配置类的结构, 解析一个 ConfigurationClass, 最终可能会被构造出任意数量的 ConfigurationClass 的实例, 这是因为 ConfigurationClass 可以被 @Import 注解或是 @ImportSelector 注解修饰, 这两个注解可相当于文件引用, 可以将其他 ConfigurationClass 的内容导入到被修饰的 ConfigurationClass 中

紧接着阅读一下 parse(Set<BeanDefinitionHolder> configCandidates) 方法, 稍微跟进一下代码, 很快进入关键方法 doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		// Recursively process any member (nested) classes first
		processMemberClasses(configClass, sourceClass);

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

在上面的代码中, 我们终于看到了非常熟悉的 @ComponentScan, @Import, @ImportResource 注释的处理。 以 @ComponentScan 为入手点, 跟进到 componentScanParser.parse( ... ) 方法中, 可以看到关键代码 scanner.doScan(…) 的调用



public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
		... 其他代码 ...
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			// 下面的 findCandidateComponents(basePackage), 将classpath 下的 basePackage 路径下的的 class 文件, 都读取解析成了 BeanDeifinition 的形式
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					// 这里会将解析出来的  BeanDefinition 注册到容器中
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

至此, 我们终于梳理出来 SpringBoot 应用在零 xml 配置文件的形式下, 解析获取 beanDefinition 的基本步骤脉络。

下面通过一张图把上面的文章中, 获取到的最核心信息总结一下。
在这里插入图片描述

创建 Bean (实例化 Bean)

上文梳理了 beanDeifinition 的获取流程, 现在可以学习 bean 的创建流程, 回到 AbstractApplicationContext 类的 refresh() 方法

			// (1) Prepare this context for refreshing.
			prepareRefresh();

			// (2) Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// (3)Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// (4)Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// (5)Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// (6)Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// (7)Initialize message source for this context.
				initMessageSource();

				// (8)Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// (9)Initialize other special beans in specific context subclasses.
				onRefresh();

				// (10)Check for listener beans and register them.
				registerListeners();

				// (11)Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// (12)Last step: publish corresponding event.
				finishRefresh();
			}

根据注释可以看到, 步骤 7 、8、9、11 涉及到 bean 的初始化(也就是实例化操作), 7、8、9 属于特殊的个别 bean 初始化,11 则完成了步骤 7、8 、9 没有涉及到的其他非懒加载的单例 bean。 具体逻辑跟进代码看下

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		... 其他代码 ...
		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
}

@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isDebugEnabled()) {
			logger.debug("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		// 这里对 DefaultListBeanFactory 中已经注册的 beanDefinitionNames 进行了拷贝
		// 然后遍历处理, 目的是确保在遍历的过程中,还能向原始的 beanDeifintionNames 中注册新的 beanDefnition, 同时不影响遍历过程
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		// 这里开始出发所有非延迟加载的单例 bean 初始化
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			// 这里判断, beanDefinition 是非抽象的, 单例的, 非延迟加载的, 才尝试进一步处理
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				// 如果是工厂 bean
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						// 查看SmartFacotryBean 这个接口, 可以看到这是一个主要用于 spring 内部的接口, 基本上不需要spring 框架使用者关心, 这里不做深究 
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) 
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
						// 如果最终判断了 factoryBean 是属于 eagerInit (迫切需要初始化的) 类型
						// 在这里就通过 getBean 方法进行初始化
							getBean(beanName);
						}
					}
				}
				else {
				// 非工厂类型的, 单例非懒加载 bean 这里直接初始化
					getBean(beanName);
				}
			}
		}

		// Trigger post-initialization callback for all applicable beans...
		// 触发在需要在初始化后, 对所有 bean (适用的 bean ) 执行的回调函数逻辑
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

从上面的代码逻辑中, 我们可以看出, beanDefinition 的初始化是通过 DefaultListableBeanFactory 的 getBean( beanName) 完成的。 于是跟进这个方法, 追踪到 AbstractBeanFactory 中定义的 doGetBean(name, null, null, false);

public Object getBean(String name) throws BeansException {
	 	return doGetBean(name, null, null, false);
}

/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * @param name the name of the bean to retrieve
	 * @param requiredType the required type of the bean to retrieve
	 * @param args arguments to use when creating a bean instance using explicit arguments
	 * (only applied when creating a new instance as opposed to retrieving an existing one)
	 * @param typeCheckOnly whether the instance is obtained for a type check,
	 * not for actual use
	 * @return an instance of the bean
	 * @throws BeansException if the bean could not be created
	 */
	@SuppressWarnings("unchecked")
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException 

doGetBean 是 bean 创建的核心方法, 这个方法的代码比较多, 这里拆分开来一点点学习

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		// 首先看这里的 transformedBeanName 方法作用
		final String beanName = transformedBeanName(name);
		Object bean;

根据 transformedBeanName 的注释,

/**
	 * Return the actual bean name, stripping out the factory dereference
	 * prefix (if any, also stripping repeated factory prefixes if found).
	 * @param name the name of the bean
	 * @return the transformed name
	 * @see BeanFactory#FACTORY_BEAN_PREFIX
	 */
	 public static String transformedBeanName(String name)

可以看出该方法的作用是, 将传入的 name 参数转换为实际的最终 beanName , 有如下转换情况

  • 转换前的 beanName 可能是一个包含 facotory bean 前缀的名称, 该方法会去除该前缀。
  • 转换前的 beanName 可能是一个包含 factory bean 前缀的别名(alias), 该方法会去除前缀, 并将别名转换为实际名称
  • 转换前的 beanName 可能是一个 bean 的别名, 该方法将别名转换为实际名称

bean 的别名机制很好理解, 但是 factory bean 的特殊前缀是一个知识点。


知识点: Spring 中的 FactoryBean ( 注意与 BeanFactory 进行区分)

在 Spring bean 中, 有两类 bean, 一类是普通的 bean, 另一类是 facotory bean。 factory bean 与普通 bean 的区别是:

  • 普通 bean (ordinary bean): 我们通常直接从 spring 容器中获取定义的 bean, 注入需要依赖它的对象
  • 工厂 bean (factory bean):是自己本身可以生产 bean 的一类工厂 bean。 我们往往最终注入的往往不是定义的工厂 bean 本身, 而是它所生产的对象。 但是, 它所生产的对象是不在 Spring 容器的管理范围之内的。

举个例子, 我们构建一个可以生产 Tool 的 ToolFactory

public class Tool {
 
    private int id;
 
    // standard constructors, getters and setters
}
public class ToolFactory implements FactoryBean<Tool> {
 
    private int factoryId;
    private int toolId;
 
    @Override
    public Tool getObject() throws Exception {
        return new Tool(toolId);
    }
 
    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }
 
    @Override
    public boolean isSingleton() {
        return false;
    }
 
    // standard setters and getters
}

在使用注解的场景下, 我们可以通过如下方式配置 bean

@Configuration
public class FactoryBeanAppConfig {
  
    @Bean(name = "toolFactory")
    public ToolFactory toolFactory() {
        ToolFactory factory = new ToolFactory();
        factory.setFactoryId(7070);
        factory.setToolId(2);
        return factory;
    }
 
    @Bean
    public Tool tool() throws Exception {
        return toolFactory().getObject();
    }
}

下面看,怎么注入工厂bean。 需要注意默认情况下, 直接

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FactoryBeanAppConfig.class)
public class FactoryBeanJavaConfigTest {
 
    @Autowired
    private Tool tool;
  
    @Resource(name = "&tool")
    private ToolFactory toolFactory;
 
    @Test
    public void testConstructWorkerByJava() {
        assertThat(tool.getId(), equalTo(2));
        assertThat(toolFactory.getFactoryId(), equalTo(7070));
    }
}


从上面这个使用流程可以看到, 最终需要注入 Tool 实例的场景, 是通过 toolFacotry.getObject() 这一方法获得的。

当我们需要获取生产 “Tool”这一类型的工厂bean本身时, 在注入域上有一个特殊的名称写法,@Resource(name = "&tool")

这里的 “&” 符号, 即是前文提到的 transformed 方法会处理的一种情况

知识点结束


回到前文正在阅读的 AbstractBeanFactory 中的 doGetBean 方法源码

		// transformedBeanName 方法作用已经基本理解
		final String beanName = transformedBeanName(name);
	    // 从这里继续看后续的逻辑
		Object bean;
		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			... 此处省略一些日志逻辑...
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}else{
			// Check if bean definition exists in this factory.
			// 检查需要获取的 bean 是否需要从 parent bean factory 中获取
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)){
				... 此处省略尝试从 parent bean facotry 获取 bean 的逻辑
				... 如果成功从 parent bean facotry 获取到到对应的 bean, 会直接 return
			}
			// 如果当前获取的 bean 并不仅仅是用于类型检查, 而是真的需要使用,此时就已经可以将这个 bean 标识为已创建了, 具体是向一个名称为 alreadyCreated 的 set 中添加该 beanName
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}
			// 程序运行到这里就说明, 该 bean 既不在已创建的bean缓存中, 也不能从 parent bean factory 中获取, 应该由当前的 beanFactory 创建
			// 下面是一个大的 try catch 块, 包含了一个 bean 的在 AbstractBeanFactory 的正式创建流程
			try {
				// 1. 获取 beanDeifinition, 做一些检查工作
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				// 2. 根据 beanDefinition 获取该 bean 的全部依赖 bean, 挨个予以创建或获取
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
					    // 在这里检查是否存在循环依赖
					    // 注意: isDependent 这个方法是在检查名称为 dep 是否依赖于 beanName, 参数的顺序上有点反直觉
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// 将这个依赖的 dep 添加到一个 map 中,将来在销毁 beanName 的时候, 要先把所有的 dep 销毁了
						registerDependentBean(dep, beanName);
						// 执行 dep 的获取/创建逻辑
						// 注意这里调用的还是 getBean 方法, 实质上是发生了递归调用
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}
				// 3. 执行到这里时, beanName 所有依赖的 bean,包括传递依赖, 肯定已经被创建了
				//    可以正式执行 beanName 的创建逻辑了
				// Create bean instance.
				if (mbd.isSingleton()) {// 如果待创建的是 单例Bean
					// 注意这里第二个参数的写法是 lamda 语法对于传统匿名内部类的表达, 这里构造的其实是 ObjectFactory 的匿名内部类
					// getSingleton 方法中还是会尝试先从已经存在的 singltonBeans 中获取
					// 找不到时, 再进行创建, 最终调用的方法就是 createBean 这个方法
					// createBean 方法的默认实现来自于 AbstractAutowireCapableBeanFactory
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					// 上面已经完成了 bean 的创建, 下面这个方法实际是在处理 factoryBean 的情况
					// 如果正在构造的是一个factory bean, 那么最终返回给外部的应该是这个 factory bean
					// 生产的实例对象 object 而不是这个 bean 本身
					// 如果正在构造的是一个普通的 bean, 那这个方法的最终就会直接返回 sharedInstance 本身
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {// 如果待创建的是原型 bean
				    // 原型 bean 的创建过程就不包含从已有的 bean 中进行寻找的过程了
				    // 因为原型 bean 的设计目的就是获取的时候即创建新实例
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {// 如果待创建的既不是单例bean, 也不是原型 bean
				      // 那么根据 beanDefinition 中的 scope 属性决定如何创建这个 bean		
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			
		}
		// 前面的代码已经完成了 bean 的获取/创建工作, 这里做一些后置的requiredType检查
		// 在必要的时候对 bean 做一下类型转换, 确保最终返回的 bean 是调用方所期待的 bean
		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}

上面的这段代码虽然很长, 但是通过逐步注释分解, 并忽略掉异常处理分支以后, 我们可以总结出它如下的几个步骤。
在这里插入图片描述
至此我们就理清创建 bean (实例化 bean)的基本流程, 更加细节的实例化流程(如何根据 beanDefinition 找到 class 文件, 实例化出一个对象, 完成初始化等)的默认实现由 AbstractAutowireCapableBeanFactory.createBean(..) 完成, 这里不做更详细的展开, 感兴趣的读者可以自行研究

总结

本文主要解析了如下的知识点:

  1. Spring 容器的 IoC 思想为何被广泛使用
  2. Spring 容器的核心 Bean, BeanDefinition, BeanFactory, ApplicationContext 的概念
  3. Spring 容器如何加载 BeanDefinition, 如何根据 BeanDefinition 实例化 Bean (以 SpringBoot 应用为例)

本文附带衍生介绍了如下知识点:
4. Spring Cloud 框架中会创建的 bootstrapServiceContext
5. FactoryBean 的概念(注意不要与 BeanFactory 混淆)

掌握了以上知识有什么用:

  1. 使用 Spring 框架的功能, 最后都会落脚于一个个负责不同功能的 Bean , 掌握了 Bean 在 Spring 中最基础的管理方式, 有助于我们在遇到问时(例如无法正常 autowire 一个 bean 时), 进行有效地排查。
  2. 可以进一步去学习 Spring 框架的延伸框架 Spring Cloud 等流行框架的启动及运行原理, 有能力对此类开箱即用的框架在必要时进行调试, 扩展乃至修改。

下一篇文章, 笔者会开始整理 Spring Cloud 体系中, 一个极大提升了开发效率的框架 OpenFeign 的基本工作原理, 其中必然会涉及到很多这篇文章所介绍的基本概念,欢迎感兴趣的读者届时关注阅读

发布了51 篇原创文章 · 获赞 264 · 访问量 25万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章