在分析源码时,默认大家已经掌握Spring框架的基本使用!如果还不会Spring的萌新,这里推荐几个Spring框架入门的教程:
学完框架,自己多练习使用,只有熟悉使用了,看源码才不那么迷茫!切不可一味的堆积课程进度快餐式学习,要反复消化哦~
下面进入正题:
熟悉Spring框架的小伙伴都知道,Spring有两大核心模块:IOC (控制反转 ) 和 AOP (面向切面编程)。对于Spring IOC,我们又通常将其称为 IOC 容器,IOC 的2个实现方式分别为依赖注入(DI)和依赖查找(DL)。
注:由于依赖查找(DL)使用的很少,因此 IOC 也被叫做依赖注入。
IOC 和 DI 、DL 的关系图:
Spring IOC 实现了依赖注入,通过一个核心的 Bean 工厂 (BeanFactory) 来负责各个 Bean 的实例化和依赖管理。各个 Bean 不需要考虑各自复杂的创建过程,进而实现解耦。
对于 IOC 来说,最重要的概念就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依赖注入。
Spring 作者 Rod Johnson 设计了两个接口用以表示容器:
BeanFactory
BeanName
,Value 是 Bean 实例
。通常只提供注册(put
),获取(get
)这两个功能。我们可以称之为 “低级容器”。ApplicationContext
refresh
方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 Bean。我们通过UML图来看一下BeanFactory
与ApplicationContext
的关系:
从UML关系图中,也可以看出ApplicationContext肯定会比BeanFactory更加复杂。在之后 Spring源码分析——容器扩展中会详细分析(努力更新中)~
如果对Spring IOC的功能进行粗略概括的话,其主要分为如下2个功能点:
BeanDefinition
的形式(即,从配置文件或者注解中获取 Bean 的定义信息解析为 BeanDefinition
对象,并为其注册一些扩展功能)。
—> BeanDefinition
:XML标签解析,总体来说主要就是完成对默认标签的4个标签进行解析,即:标签
、标签
、标签(最为复杂)
、标签
。其中的过程弯弯绕绕,不过我们只要清楚,目的是将XML 配置文件中的配置转换为 BeanDefinition 对象。@Bean
—> BeanDefinition
BeanDefinition
)获取 Bean 对象实例。
BeanDefinition
—> Bean
对象如下图所示:
本文主要是对Bean加载这一流程进行解析,而XML标签的解析加载,这里不作为重点!
先来看一个Demo:
声明2个要注册到IOC中的对象ComponentA
、ComponentB
public class ComponentA {
}
public class ComponentB {
}
测试从IOC中获取这两个Bean对象:
public class BeanFactoryTest {
public static void main(String[] args) {
// 加载与解析XML配置文件,获得BeanFactory:
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-bf.xml"));
// 从BeanFactory中获取Bean对象
Object a = beanFactory.getBean("componentA");
Object b = beanFactory.getBean("componentB");
System.out.println(a);// com.myspring.test.xmltest.ComponentA@1c93084c
System.out.println(b);// com.myspring.test.xmltest.ComponentB@6ef888f6
}
}
由上面的Demo我们可以知道,Spring通过调用 BeanFactory
的 getBean()
方法来加载 Bean,那么我们进入 AbstractBeanFactory
来看一下源码:
/**
* 根据参数name,requiredType,args获取Bean对象实例:
*
* @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
* @param requiredType 检索所需的Bean类型 -> 获取Bean对象时,不仅要根据name去检索,还要判断Bean的类型是否一致
* @param args 这个参数用到再做分析
* @return 该方法返回目标Bean的一个实例
* @throws BeansException if the bean could not be created
*/
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException {
// 调用真正去获取Bean对象的方法:
// 注:Spring框架源码的命名规范非常严谨,doXxx方法(内层方法)内封装的是具体执行逻辑的代码,而调用doXxx的方法是其外层方法
return doGetBean(name, requiredType, args, false);
}
/**
* 真正去获取Bean对象的方法:
* @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
* @param requiredType 检索所需的Bean类型
* @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
* @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
* @return 返回Bean的一个实例
* @throws BeansException if the bean could not be created
*/
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// transformedBeanName(name)方法:根据传入的name参数,获取真正的Bean对应的beanName,什么意思呢?
// Spring中管理的Beand对象是可以指定设置别名的,Spring Bean设置别名的两种方式:参考 https://blog.csdn.net/qq_34129814/article/details/7
// 参数传进来的name,有可能是一个别名(eg: alias设置的别名),也有可能是一个&开头的name(解释如下):
// (1)别名name(eg: alias设置的别名),transformedBeanName(name)方法就是通过别名重定向出来真实beanName名称
// (2)&开头的name,说明,你要获取的Bean实例对象,是一个FactoryBean对象。
// FactoryBean:如果某个Bean的配置非常复杂,使用Spring管理不容易、不够灵活,想要使用编码的形式去构建它,
// 那么你就可以提供一个构建该Bean实例的工厂,这个工厂就是FactoryBean接口实现类。FactoryBean接口实现类还是需要使用Spring管理的。
// 这里就涉及到两种对象,一种是FactoryBean接口实现类(IOC管理的),另一个就是FactoryBean接口内部管理的对象。
// 如果要拿FactoryBean接口实现类,使用getBean时传的beanName需要带“&”开头。
// 如果你要FactoryBean内部管理的对象,你直接传beanName不需要带“&”开头。
String beanName = transformedBeanName(name);
// 用于保留返回值(要返回的Bean实例对象)
Object beanInstance;
// 根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
// 注:
// 第一个getSingleton(String beanName)方法是一个参数的,
// 后面还有一个重载的getSingleton方法(2个参数),2个不要搞混了!
Object sharedInstance = getSingleton(beanName);
// CASE1:
// 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean,所以需要根据name来进行判断
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 这里为什么又要套呢?为啥不直接拿回去用呢?
// 其实,你从IOC中拿到的对象,它可能是普通单实例,也可能是FactoryBean实例。
// 如果是FactoryBean实例,这个时候还要进行处理。主要是看name是带“&” 还是不带“&”,
// 带“&”:则说明这次getBean方法想要拿FactoryBean对象。
// 不带“&”:则说明是要拿FactoryBean内部管理的实例。
/**
* 获取给定Bean实例的对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
* 方法参数:
* sharedInstance: 缓存中拿到的单实例对象
* name: 未处理“&”的name
* beanName: 处理过“&”和别名后的name
* mbd: 合并过后的bd(BeanDefinition)信息。
*/
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// CASE2:
// 如果根据beanName从缓存中没有找到对应的数据,那么我们就需要自己创建了...
else {
// 一、原型循环依赖问题判定:创建Bean时,判断是否出现循环依赖的情况
// 举个例子:
// prototypeA -> B, B -> prototypeA
// 1.会向正在创建中的原型集合内添加一个字符串 “A”
// 2.创建prototypeA对象,只是一个早期对象。
// 3.处理prototypeA的依赖,发现A依赖了B类型的对象
// 4.触发了Spring.getBean(“B”)的操作。
// 5.根据B的构造方法反射创建出来了B的早期实例
// 6.Spring处理B对象的依赖,发现依赖了A。
// 7.Spring转头回来再次去获取A去了。getBean(“A”).
// 8.条件会返回true,最终抛出异常,算是结束了循环依赖注入。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// 如果beanDefinitionMap中也就是已经加载的类中不包括beanName则尝试从parentBeanFactory中检测
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
// 递归到BeanFactory中寻找
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// 如果不仅仅是做类型检查则是创建Bean,这里要进行记录
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
}
// 二、获取合并BD信息
// 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
// 如果指定BeanName是子Bean的话同时会合并父类的相关属性
// 为什么需要合并呀?因为BD支持继承
// BD: 在XML配置文件中用 parent 属性可以定义父 和子 ,父 用 RootBeanDefinition表示
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 判断当前BD是否为抽象BD,抽象BD不能创建实例,只能作为父BD让子BD去继承。
checkMergedBeanDefinition(mbd, beanName, args);
// 三、depends-on属性处理..
//
//
// 循环依赖问题
//
//
// Spring是处理不了这种情况的,需要报错..
// Spring需要发现这种情况的产生。
// 怎么发现呢? 依靠两个Map,一个map是 dependentBeanMap 另一个是 dependenciesForBeanMap
// 1. dependentBeanMap 记录依赖当前beanName的其他beanName
// 2. dependenciesForBeanMap 记录当前beanName依赖的其它beanName集合
String[] dependsOn = mbd.getDependsOn();
// 若存在依赖则需要递归实例化依赖的bean
if (dependsOn != null) {
for (String dep : dependsOn) {
// 判断循环依赖..
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'
}
// 假设
// dep:B,beanName:A
// 以B为视角 dependentBeanMap {"B":{"A"}}
// 以A为视角 dependenciesForBeanMap {"A" :{"B"}}
// 缓存依赖调用
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// CASE-SINGLETON:Create bean instance.
// 创建单实例的逻辑
// 实例化依赖的Bean后便可以实例化mbd本身了(singleton模式的创建)
if (mbd.isSingleton()) {
// 第二个getSingleton方法,这个方法更倾向于创建实例并返回:
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建单例Bean的核心方法
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;
}
});
//这 里为啥不直接返回,还调用getObjectForBeanInstance(...)?
// 这里为什么又要套呢?为啥不直接拿回去用呢?
// 其实,你从IOC中拿到的对象,它可能是普通单实例,也可能是FactoryBean实例。
// 如果是FactoryBean实例,这个时候还要进行处理。主要是看name是带“&” 还是 不带“&”,
// 带“&”说明这次getBean想要拿FactoryBean对象。
// 否则是要拿FactoryBean内部管理的实例。
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// CASE-PROTOTYPE: 创建多实例
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
// prototype原型模式的创建(new)
Object prototypeInstance = null;
try {
// 记录当前线程相关的正在创建的原型对象beanName
beforePrototypeCreation(beanName);
// createBean方法创建对象
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 从正在创建中的集合中移除beanName对应的Bean。
afterPrototypeCreation(beanName);
}
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// CASE-OTHER: 这一情况笔记复杂,不做分析!
else {
// 指定的scope上实例化bean
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
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);
}
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
}
}
}
catch (BeansException ex) {
beanCreation.tag("exception", ex.getClass().toString());
beanCreation.tag("message", String.valueOf(ex.getMessage()));
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
finally {
beanCreation.end();
}
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
getBean()
外层方法去调用doGetBean()
内层方法,这种嵌套结构在Spring框架源码中非常常见。上面是跟踪了 getBean
的调用链创建的流程图,为了能够很好地理解 Bean 加载流程,省略一些异常、日志和分支处理和一些特殊条件的判断。
从上面的流程图中,可以看到一个 Bean 加载会经历这么几个阶段(用绿色标记):
BeanDefinition
的 beanName。getBean
方法获取。FactoryBean
需要调用getObject
方法,如果需要类型转换调用 TypeConverter
进行转化。transformedBeanName(name)
该方法的作用是,根据传入的 name 参数,获取真正的 Bean 对应的 beanName。该方法的 name 参数,有可能是一个别名(alias 属性设置的别名),也有可能是一个&
开头的 name:
① 别名name(alias 属性设置的别名),transformedBeanName(name)
方法就是通过别名重定向出来真实beanName 名称的!Spring Bean设置别名的两种方式如下:
<!--
方式一:
*alias* 属性设置别名, 使用alias设置别名,alias的 name 要和 bean 的 ID 相同, 可以设置多个别名!
-->
<bean id="addOne" class="...">
<alias name="addOne" alias="add"/>
<alias name="addOne" alias="doAdd"/>
</bean>
<!--
方式二:
*name* 属性设置别名, 多个别名用逗号隔开!
-->
<bean id="addOne" class="..." name="add,doAdd"">
</bean>
② &
符号开头的 name ,说明,你要获取的 Bean 实例对象,是一个FactoryBean对象。
getBean("&beanName");// 这种情况获取的就是一个 FactoryBean 对象
获取真正的beanName的步骤如下:
FactoryBean
的修饰符,一般就是去除 name 参数的 &
前缀;/**
* 真正去获取Bean对象的方法:
* @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
* @param requiredType 检索所需的Bean类型
* @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
* @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
* @return 返回Bean的一个实例
* @throws BeansException if the bean could not be created
*/
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 根据传入的name参数,获取真正的Bean对应的beanName
String beanName = transformedBeanName(name);
// 用于保留返回值(要返回的Bean实例对象)
Object beanInstance;
...
}
我们跟进transformedBeanName(name);
方法内部查看:
/**
* Return the bean name, stripping out the factory dereference prefix if necessary,
* and resolving aliases to canonical names.
* (翻译:返回Bean的真实名称,必要时去除工厂引用前缀,并将别名解析为规范名称)
*
* 根据传入的name参数(该参数可能是别名,也可能是&开头的name),获取真正的Bean对应的beanName:
*
* @param name 获取Bean对象时,要传入的name参数(该参数可能是别名,也可能是&开头的name)
* @return 返回转换后真正的Bean名称(beanName)
*/
protected String transformedBeanName(String name) {
// 1.BeanFactoryUtils.transformedBeanName(name) 调用工厂工具类的转换方法:
// 根据传入的name参数去获取转换后的name参数(去掉"&"符号):eg -> name="&abc" 则转换后获得 name="abc"
// 2.canonicalName方法 -> 根据Bean的别名name,获取真实的beanName:
// BeanFactoryUtils.transformedBeanName(name)方法转换后的name是最终值么?
// 不是!BeanFactoryUtils.transformedBeanName(name)方法仅仅是去掉name参数的"&"符号而已!
// 转换后得到的name还有可能是alias属性指定的别名:
// aliasMap保存别名信息:
// {"C":"B", "B":"A"} A有一个别名叫做“B”,但是别名“B” 它又被别名了,它有一个别名叫做“C”
// 假设调用getBean(name)方法时,name传的是“C”,那最终要得到什么? 要得到“A”(真正的beanName),而不是"B"!
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
BeanFactoryUtils.transformedBeanName(name)
方法去除 name 参数中的 FactoryBean
的修饰符&
:
/**
* Return the actual bean name, stripping out the factory dereference
* prefix (if any, also stripping repeated factory prefixes if found).
* (翻译:返回实际的bean名称,删除工厂取消引用前缀(如果有的话,还删除重复的工厂前缀(如果找到)))
*
* 根据传入的name参数去获取转换后的name参数(去掉"&"符号):eg -> name="&abc" 则转换后获得 name="abc"
* @param name 获取Bean对象时,要传入的name参数(该参数可能是别名,也可能是&开头的name)
* @return 返回转换后真正的Bean名称(beanName)
* @see BeanFactory#FACTORY_BEAN_PREFIX
*/
// 位于BeanFactoryUtils工具类中的方法
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
// 如果条件成立:说明调用getBean方法时传的参数name,不是“&”符号开头的,所以直接返回name即可
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
// 执行到这里,说明name一定是“&”符号开头的,说明这次getBean方法就是要拿封装为Bean的FactoryBean实例对象(有点套娃的感觉)!
// 调用transformedBeanNameCache方法将去掉“&”符号得到的结果缓存起来。提高后续程序运行性能的。
// 科普一下:map.computeIfAbsent(key, value)方法
// 当map中对应的key为null或者 key->value是null时,这次put操作就会成功,写成功也会返回value。
// 否则就会失败,并且返回原有key->value。
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
// 假设 name="&abc" 则转换后获得 name="abc"(将其返回)
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}
canonicalName
方法,取指定的 alias 所表示的最终 beanName:
/**
* Determine the raw name, resolving aliases to canonical names.
* (翻译:确定原始名称,将别名解析为规范名称。)
*
* 根据Bean的别名name,获取真实的beanName:
* @param name 根据Bean的别名name
* @return 返回真实的beanName
*/
// 位于SimpleAliasRegistry类中的方法:
public String canonicalName(String name) {
String canonicalName = name;
String resolvedName;
do {
// aliasName 和 beanName 的映射关系被注册到 SimpleAliasRegistry 中,
// do-while循环从该注册器中根据 aliasName 取到 真实的 beanName:
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
/** Map from alias to canonical name. */
// 位于SimpleAliasRegistry类中的成员属性:存储 映射别名和真实beanName的集合
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
根据上面transformedBeanName
方法转换 name 后得到的真实 beanName,getSingleton(beanName)
方法直接尝试从缓存中获取 Bean 的共享单实例(单例):
/**
* 真正去获取Bean对象的方法:
* @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
* @param requiredType 检索所需的Bean类型
* @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
* @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
* @return 返回Bean的一个实例
* @throws BeansException if the bean could not be created
*/
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 根据传入的name参数,获取真正的Bean对应的beanName
String beanName = transformedBeanName(name);
// 用于保留返回值(要返回的Bean实例对象)
Object beanInstance;
// 根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
Object sharedInstance = getSingleton(beanName);
...
}
在分析getSingleton(beanName)
方法之前,我们先来了解下Spring IOC中的三级缓存,以及三级缓存是如何解决循环依赖问题的:
所谓三级缓存,其实就是org.springframework.beans.factory
包下DefaultSingletonBeanRegistry
类中的三个成员属性:
// Spring一级缓存:用来保存实例化、初始化都完成的对象
// Key:beanName
// Value: Bean实例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// Spring二级缓存:用来保存实例化完成,但是未初始化完成的对象
// Key:beanName
// Value: Bean实例
// 和一级缓存一样也是保存BeanName和创建bean实例之间的关系,
// 与singletonObjects不同之处在于,当一个单例bean被放在里面后,
// 那么bean还在创建过程中,就可以通过getBean方法获取到了,其目的是用来循环检测引用!(后面会分析)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// Spring三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象
// Key:beanName
// Value: Bean的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
如图所示,除了三级缓存是一个HashMap,其他两个都是ConcurrentHashMap:
Spring之所以引入三级缓存,目的就是为了解决循环依赖问题!
除了上面三个Map集合,还有另一个集合这里也说一下:
// 用来保存当前所有已注册的Bean
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
首先,Spring 解决循环依赖有两个前提条件:
本质上解决循环依赖的问题就是依靠三级缓存,通过三级缓存提前拿到未初始化的对象。下面我们来看一个循环依赖的例子:
A 对象的创建过程:
创建对象A,实例化的时候把A对象工厂放入三级缓存
A 注入属性时,发现依赖 B,转而去实例化 B
同样创建对象 B,注入属性时发现依赖 A,一次从一级到三级缓存查询 A,从三级缓存通过对象工厂拿到 A,把 A 放入二级缓存,同时删除三级缓存中的 A,此时,B 已经实例化并且初始化完成,把 B 放入一级缓存。
接着继续创建 A,顺利从一级缓存拿到实例化且初始化完成的 B 对象,A 对象创建也完成,删除二级缓存中的 A,同时把 A 放入一级缓存。
最后,一级缓存中保存着实例化、初始化都完成的A、B 对象。
解决循环依赖为什么必须要要三级缓存?二级不行吗?
答案:不可以!
使用三级缓存而非二级缓存并不是因为只有三级缓存才能解决循环引用问题,其实二级缓存同样也能很好解决循环引用问题。
使用三级而非二级缓存并非出于IOC的考虑,而是出于AOP的考虑,即若使用二级缓存,在AOP情形下,往二级缓存中放一个普通的Bean对象,BeanPostProcessor
去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致了。
了解完什么是循环依赖和三级缓存后,我们继续分析getSingleton(beanName)
方法,代码如下:
/**
* 根据真实beanName到缓存中获取Bean的共享单实例(单例):
*
* @param beanName Bean的真实beanName
* @return
*/
@Override
@Nullable
public Object getSingleton(String beanName) {
// 第二个参数为true表示允许早期依赖
return getSingleton(beanName, true);
}
/**
* 根据真实beanName到缓存中获取Bean的共享单实例(单例):
*
* @param beanName Bean的真实beanName
* @param allowEarlyReference 是否应创建早期参考
* @return 注册的单例对象;如果找不到,则返回null
*/
// 位于DefaultSingletonBeanRegistry类中
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先到一级缓存中尝试获取beanName对应的单实例Bean对象:
Object singletonObject = this.singletonObjects.get(beanName);
// 如果条件一成立(singletonObject == null):有几种可能呢?(2种)
// 1.单实例确实尚未创建...
// 2.单实例正在创建中,当前发生循环依赖了...
// 条件一成立,条件二也会成立:
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 尝试从二级缓存中获取beanName对应的单实例Bean对象:
singletonObject = this.earlySingletonObjects.get(beanName);
// 条件成立:说明二级缓存没有找到~
// 再尝试从三级缓存中获取beanName对应的单实例Bean对象:
if (singletonObject == null && allowEarlyReference) {
// 如果缓存中不存在,锁住一级缓存singletonObjects
synchronized (this.singletonObjects) {
// 这里再次从一级缓存中去检索beanName,是为了防止此时有其他线程给一级缓存中添加了该Bean对象(导致加锁对象singletonObjects被修改)
singletonObject = this.singletonObjects.get(beanName);
// 这里singletonObject == null 依然成立,说明没有其他线程修改singletonObjects中的缓存数据,则继续向下走!
if (singletonObject == null) {
// 同理:从二级缓存中去检索beanName
singletonObject = this.earlySingletonObjects.get(beanName);
// 同理:这里singletonObject == null 依然成立,说明没有其他线程修改singletonObjects中的缓存数据,则继续向下走!
if (singletonObject == null) {
// 从三级缓存中检索beanName
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 如果条件成立:说明3级缓存中有beanName对应的数据,这里涉及到缓存升级~
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 记录在缓存中,earlySingletonObjects和singletonFactories互斥!
// 向2级缓存中存入该数据
this.earlySingletonObjects.put(beanName, singletonObject);
// 将3级缓存的该数据干掉
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
总结一下上面代码的执行过程:
singletonObjects
获取实例earlySingletonObjects
获取singletonFactories
中获取beanName对应的ObjectFactorygetObject
方法来创建Bean,放到earlySingletonObjects
,并从singletonFactories
移除掉这个ObjectFactory总之这一块很繁琐,Spring源码阅读就是很少脑子,真不知设计者是怎样的天才啊!不感叹了,我们接这这个方法结束后,接这回到doGetBean()
方法继续往下阅读代码:
getSingleton(beanName)
方法执行后,从缓存中得到了 Bean 的原始状态,接下来需要对该 Bean 进行实例化。
缓存中记录的只是最原始的 Bean 状态,并不是我们最终想要的 Bean!
getObjectForBeanInstance
方法,其实就是检测获得 Bean 是不是 FactoryBean 类型的 Bean。如果是,那么需要调用该 Bean 对应的 FactoryBean
实例中的 getObject()
作为返回值。factory-method
方法中返回的 Bean,而getObjectForBeanInstance
方法就是完成这个操作。/**
* 真正去获取Bean对象的方法:
* @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
* @param requiredType 检索所需的Bean类型
* @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
* @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
* @return 返回Bean的一个实例
* @throws BeansException if the bean could not be created
*/
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 根据传入的name参数,获取真正的Bean对应的beanName
String beanName = transformedBeanName(name);
// 用于保留返回值(要返回的Bean实例对象)
Object beanInstance;
// 根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
Object sharedInstance = getSingleton(beanName);
// CASE1:
// 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean(需要根据name来进行判断)
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
... // 这里的代码只是打印日志,就不再加了
}
// 获取sharedInstance对应的Bean实例化对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
...
}
getObjectForBeanInstance(sharedInstance, name, beanName, null);
中为什么又要传入未处理过&
的 name 呢?因为,我们从 IOC 中拿到的对象,它可能是普通单实例,也可能是 FactoryBean
实例。如果是FactoryBean
实例,这个时候还要进行处理。主要是看 name 是否带&
符号。
&
符号:则说明这次getBean()
方法想要拿FactoryBean
对象。&
符号:则说明是要拿FactoryBean
内部管理的实例。我们跟进``getObjectForBeanInstance`方法中分析详细一下:
/**
* Get the object for the given bean instance, either the bean
* instance itself or its created object in case of a FactoryBean.
* (翻译:获取给定Bean实例的对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象)
*
* 获取给定Bean实例的对象,如果是FactoryBean类型,则可以获取该实例本身或其创建的子Bean对象。
* @param beanInstance 缓存中拿到的单实例对象(初始化状态,尚未实例化)
* @param name 未处转换处理过的name(可能带有&工厂符号)
* @param beanName 经过转换处理过的真实beanName
* @param mbd 合并过后的bd(BeanDefinition)对象
* @return the object to expose for the bean
*/
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 如果条件成立:说明当前要获取FactoryBean对象,即name中带有&符号:
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果beanInstance是null,则直接返回
if (beanInstance instanceof NullBean) {
// 直接返回
return beanInstance;
}
// 如果条件成立:说明单实例对象不是 FactoryBean接口实现类,则验证不通过,直接抛异常!
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
// 给当前Bean实例对应的mbd打个标记,记录它表达的实例是一个FactoryBean.
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// 执行到这里,有几种情况呢?(2种)
// 情况1.当前Bean实例就是普通单实例
// 情况2.当前Bean实例是FactoryBean接口实现类,但是本次请求要拿的是FactoryBean实现类内部管理的实例。
// 1.当前bean实例就是普通单实例
if (!(beanInstance instanceof FactoryBean)) {
// 直接返回该单实例
return beanInstance;
}
// 2.当前Bean实例是FactoryBean接口实现类,但是本次请求要拿的是FactoryBean实现类内部管理的实例。
// 用于保存FactoryBean实例调用getObject方法后得到的返回值.
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
// 尝试从缓存中加载Bean,如果缓存中已经存在 beanName 对应的工厂 Bean 生成的对象,则直接返回
// (即,尝试到缓存获取FactoryBean.getObject返回值)
object = getCachedObjectForFactoryBean(beanName);
}
// 如果条件成立:说明缓存中没有,就需要到FactoryBean.getObject获取
if (object == null) {
// 到这里已经明确知道beanInstance一定是FactoryBean类型
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// 判断Spring中是否有当前beanName对应的BD信息:
// containsBeanDefinition检测beanDefinitionMap中也就是在所有已经加载的类中检测是否定义beanName
if (mbd == null && containsBeanDefinition(beanName)) {
// 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
// 如果指定BeanName是子Bean的话同时会合并父类的相关属性
// 为什么是合并后的呢?因为咱们的BD是直接继承的。合并后的BD信息是包含继承回来的BD。
mbd = getMergedLocalBeanDefinition(beanName);
}
// 是否是用户定义的而不是应用程序本身定义的:
// synthetic 默认值是false 表示这是一个用户对象,如果是true 表示是系统对象。
boolean synthetic = (mbd != null && mbd.isSynthetic());
/**
* getObjectFromFactoryBean是本方法的核心,从给定的FactoryBean中获取一个Bean对象:
*
* factory:FactoryBean实例
* beanName:FactoryBean实例中的Bean对应的beanName
* synthetic:如果是用户对象,这个值就是true,大部分情况下都是true。
*/
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
总的来说,getObjectForBeanInstance
中做了如下几件事情:
getObjectFromFactoryBean(factory, beanName, !synthetic)
方法。下面我们继续跟进getObjectFromFactoryBean(factory, beanName, !synthetic)
方法:
/**
* 从给定的FactoryBean中获取一个Bean对象:
* @param factory FactoryBean实例
* @param beanName FactoryBean实例中的Bean对应的beanName
* @param shouldPostProcess 如果是用户对象,这个值就是true,大部分情况下都是true。
* @return 返回从FactoryBean获得的Bean对象
* @throws BeanCreationException if FactoryBean object creation failed
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
// 位于FactoryBeanRegistrySupport中的方法:
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// 如果条件成立:说明factory是单例的同时,factory还得已经被实例化
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 再次尝试从缓存中获取bean
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 委托doGetObjectFromFactoryBean方法产生Bean
object = doGetObjectFromFactoryBean(factory, beanName);
// 再次判断缓存中是存在beanName对应的Bean
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
beforeSingletonCreation(beanName);
try {
// 调用ObjectFactory的后处理器
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
}
}
// 单例模式下:已经加载的bean要记录下来,便于下次复用
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
如果返回的 Bean 是单例的,就必须保证全局唯一,同时不可重复创建,可以使用缓存来提高性能,也就是说加载过就记录下来以便于下次复用。如果返回的 Bean 不是单例的,则直接创建 Bean。
Spring是怎么进行保证单例模式下 Bean 的全局唯一呢?在代码 16 和 19 行,分别在创建 Bean 前后进行双重判断缓存中是否能通过 beanName 获取到实例。
这里的缓存就是 this.factoryBeanObjectCache
这个 ConcurrentHashMap
。该方法在创建完成 Bean 后,最后会将其添加至缓存。
我们大致讲了 getObjectFromFactoryBean()
方法的前面和末尾的流程,其中创建 Bean 的工作实际交给了 doGetObjectFromFactoryBean(factory, beanName)
方法去完成。
又是doXxx
方法,我们前面说过,Spring框架中经常出现Xxx
方法和doXxx
方法,内外层配套使用,真正执行代码逻辑的是内层doXxx
方法。
/**
* 真正从FactoryBean中获取一个Bean实例的方法
* @param factory FactoryBean实例
* @param beanName FactoryBean实例中的Bean对应的beanName
* @return 返回从FactoryBean获得的Bean对象
* @throws BeanCreationException if FactoryBean object creation failed
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
// 作为要返回的结果
Object object;
try {
// 需要权限认证(略)
if (System.getSecurityManager() != null) {
...
}
else {
// factory.getObject()方法获取bean
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
// 略}
catch (Throwable ex) {
// 略}
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
...
}
object = new NullBean();
}
return object;
}
从上面的代码中,我们可以清晰的看到调用了 FactoryBean 的 getObject()
方法获得 Bean实例。
我们再回到 getObjectFromFactoryBean()
方法,通过 doGetObjectFromFactoryBean(factory, beanName)
方法创建出 Bean 并没有直接返回,首先再次判断缓存中是否有,如果有那么用缓存的 Bean 返回,没问题。
如果确认缓存没有,那么理论上应该直接返回,但是并没有。Spring还调用 ObjectFactory 的后处理器对 Bean 进行处理。(即,子类 AbstractAutowireCapableBeanFactory
的 postProcessObjectFromFactoryBean(object, beanName)
方法)
下面回到doGetBean()
方法继续往下跟进代码逻辑:
/**
* 真正去获取Bean对象的方法:
* @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
* @param requiredType 检索所需的Bean类型
* @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
* @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
* @return 返回Bean的一个实例
* @throws BeansException if the bean could not be created
*/
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 1.根据传入的name参数,获取真正的Bean对应的beanName
String beanName = transformedBeanName(name);
// 用于保留返回值(要返回的Bean实例对象)
Object beanInstance;
// 2.根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
Object sharedInstance = getSingleton(beanName);
// CASE1:
// 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean(需要根据name来进行判断)
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
... // 这里的代码只是打印日志,就不再加了
}
// 3.Bean的实例化
// 获取sharedInstance对应的Bean实例化对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// CASE2:
// 如果根据beanName从缓存中没有找到对应的数据 (那么我们就需要自己创建了...)
else {
// 4.原型模式(prototype)的依赖检查: Spring只能给解决单例模式下的循环依赖问题,
// 如果原型模式(prototype)出现循环依赖,则直接抛异常~
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 5.检测parentBeanFactory
// 如果beanDefinitionMap中也就是已经加载的类中不包括beanName,则尝试从parentBeanFactory中检测
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
...
}
...
}
}
在上一篇Spring源码——Bean加载(一) 3.2.2小节,我们分析了什么循环依赖,以及Spring如何解决循环依赖问题。从中得出的结论是:
因此上面代码中原型模式(prototype)的依赖检查,目的就是检测原型模式下是否出现循环依赖问题,如果出现,则Spring直接抛出异常throw new BeanCurrentlyInCreationException(beanName);
// 3.原型模式(prototype)的依赖检查: Spring只能给解决单例模式下的循环依赖问题,
// 如果原型模式(prototype)出现循环依赖,则直接抛异常~
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
如果第2步getSingleton(beanName);
从缓存中没有检索到 beanName 对应的结果,那么就转向父工厂去检索。getParentBeanFactory();
方法就是获取父工厂:
@Override
@Nullable
public BeanFactory getParentBeanFactory() {
return this.parentBeanFactory;
}
/** Parent bean factory, for bean inheritance support. */
// 父Bean工厂,用于支持Bean的继承
@Nullable
private BeanFactory parentBeanFactory;
containsBeanDefinition(beanName)
方法的作用是获取当前 BeanDefinitionMap (XML配置文件)中的 beanName 对应的配置。如果获取到则返回 true
,否则返回false
:
// 如果条件成立,说明:父工厂不为空,且当前BeanDefinitionMap中无法获取beanName对应的Bean对象
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 那么就只能到父工程parentBeanFactory中尝试去获取Bean了
// parentBeanFactory.doGetBean() 相当于递归调用~
...
}
完整代码如下:
// 如果beanDefinitionMap中也就是已经加载的类中不包括beanName则尝试从parentBeanFactory中检测
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
// 递归到BeanFactory中寻找
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
下面继续回到doGetBean()
方法往下跟进:
/**
* 真正去获取Bean对象的方法:
* @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
* @param requiredType 检索所需的Bean类型
* @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
* @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
* @return 返回Bean的一个实例
* @throws BeansException if the bean could not be created
*/
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 1.根据传入的name参数,获取真正的Bean对应的beanName
String beanName = transformedBeanName(name);
// 用于保留返回值(要返回的Bean实例对象)
Object beanInstance;
// 2.根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
Object sharedInstance = getSingleton(beanName);
// CASE1:
// 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean(需要根据name来进行判断)
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
... // 这里的代码只是打印日志,就不再加了
}
// 3.Bean的实例化
// 获取sharedInstance对应的Bean实例化对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// CASE2:
// 如果根据beanName从缓存中没有找到对应的数据 (那么我们就需要自己创建了...)
else {
// 4.原型模式(prototype)的依赖检查: Spring只能给解决单例模式下的循环依赖问题,
// 如果原型模式(prototype)出现循环依赖,则直接抛异常~
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 5.检测parentBeanFactory
// 如果beanDefinitionMap中也就是已经加载的类中不包括beanName,则尝试从parentBeanFactory中检测
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
...
}
// 如果从缓存中获取不到, 同时当前的 beanDefinitionMap 中存在对应 beanName 的配置,
// 我们就可以依据包含有XML配置文件信息的 beanDefinition 进行创建 bean 了。
try {
...
// 6.GenericBeanDefinition转为RootBeanDefinition:
// 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
// 如果指定BeanName是子Bean的话同时会合并父类的相关属性
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 判断当前BD是否为抽象BD,抽象BD不能创建实例,只能作为父BD让子BD去继承。
checkMergedBeanDefinition(mbd, beanName, args);
// 7.寻找依赖
String[] dependsOn = mbd.getDependsOn();
// 若存在依赖则需要递归实例化依赖的bean
if (dependsOn != null) {
...
}
...
catch (BeansException ex) {
...
}
finally {
beanCreation.end();
}
}
}
如果从缓存中获取不到,且当前的 beanDefinitionMap 中存在对应 beanName 的配置,我们就可以依据包含有XML配置文件信息的 beanDefinition 进行创建 Bean !
从XML配置文件中读取到的 Bean 信息是利用 GenericBeanDefinition
存储的,但是后面Spring对所有 Bean 的后续处理都是针对于 RootBeanDefinition 的,所以需要进行转换,转换的同时如果父类 Bean 不为空,则会一并合并父类的属性。
// 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
// 如果指定BeanName是子Bean的话同时会合并父类的相关属性
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 判断当前BD是否为抽象BD,抽象BD不能创建实例,只能作为父BD让子BD去继承。
checkMergedBeanDefinition(mbd, beanName, args);
因为 Bean 的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置的成依赖于其他的 Bean,那么此时应该先加载依赖的 Bean。所以在流程中,Spring初始化一个 Bean,会先初始化其依赖的所有的其他 Bean。
// 7.寻找依赖
// eg:
String[] dependsOn = mbd.getDependsOn();
// 若存在依赖则需要递归实例化依赖的Bean
if (dependsOn != null) {
for (String dep : dependsOn) {
// 判断循环依赖:\
//
//
// Spring是处理不了这种情况的,需要抛异常...
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 缓存依赖调用
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
Spring中包含如下几个作用域:
singleton
作用域prototype
作用域request
作用域session
作用域global session
作用域我们重点分析单例singleton
作用域创建Bean的逻辑,其他几种情况与其类似,不再重复分析:
// 8.根据不同的scope作用域创建Bean
// CASE-SINGLETON:创建单实例Bean的逻辑(singleton模式的创建)
if (mbd.isSingleton()) {
// 第二个getSingleton方法,这个方法更倾向于创建实例并返回:
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建单例Bean的核心方法
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put ther
// 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;
}
});
//这 里为啥不直接返回,还调用getObjectForBeanInstance(...)?
// 这里为什么又要套呢?为啥不直接拿回去用呢?
// 其实,你从IOC中拿到的对象,它可能是普通单实例,也可能是FactoryBean实例。
// 如果是FactoryBean实例,这个时候还要进行处理。主要是看name是带“&” 还是 不带“&”,
// 带“&”说明这次getBean想要拿FactoryBean对象。
// 否则是要拿FactoryBean内部管理的实例。
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// CASE-PROTOTYPE: 创建多实例Bean的逻辑(prototype模式的创建)
else if (mbd.isPrototype()) {
// prototype原型模式的创建(new)
.....
}
// CASE-OTHER:其他scope作用域创建Bean的逻辑,例如request、session、global session
else {
// 指定的scope上实例化bean
.....
}
根据 scope 创建完 Bean 成功后,一般可以直接返回即可。但当传入 doGetBean
方法中的 requireType
参数不为空时,意味着我们对最后返回的 Bean 有着类型上的要求。Spring一般通过类型转换器将第⑧步创建完成的 Bean 转换为 requireType
指定的类型。Spring自身提供了一些转换器,用户也可以自己扩展转换器来满足需求。
我们先回到doGetBean
方法再来你全局看一下:
/**
* 真正去获取Bean对象的方法:
* @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
* @param requiredType 检索所需的Bean类型
* @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
* @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
* @return 返回Bean的一个实例
* @throws BeansException if the bean could not be created
*/
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 1.根据传入的name参数,获取真正的Bean对应的beanName
String beanName = transformedBeanName(name);
// 用于保留返回值(要返回的Bean实例对象)
Object beanInstance;
// 2.根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
Object sharedInstance = getSingleton(beanName);
// CASE1:
// 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean(需要根据name来进行判断)
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
... // 这里的代码只是打印日志,就不再加了
}
// 3.Bean的实例化
// 获取sharedInstance对应的Bean实例化对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// CASE2:
// 如果根据beanName从缓存中没有找到对应的数据 (那么我们就需要自己创建了...)
else {
// 4.原型模式(prototype)的依赖检查: Spring只能给解决单例模式下的循环依赖问题,
// 如果原型模式(prototype)出现循环依赖,则直接抛异常~
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 5.检测parentBeanFactory
// 如果beanDefinitionMap中也就是已经加载的类中不包括beanName,则尝试从parentBeanFactory中检测
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
...
}
// 如果从缓存中获取不到, 同时当前的 beanDefinitionMap 中存在对应 beanName 的配置,
// 我们就可以依据包含有XML配置文件信息的 beanDefinition 进行创建 bean 了。
try {
...
// 6.GenericBeanDefinition转为RootBeanDefinition:
// 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
// 如果指定BeanName是子Bean的话同时会合并父类的相关属性
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 判断当前BD是否为抽象BD,抽象BD不能创建实例,只能作为父BD让子BD去继承。
checkMergedBeanDefinition(mbd, beanName, args);
// 7.寻找依赖
String[] dependsOn = mbd.getDependsOn();
// 若存在依赖则需要递归实例化依赖的bean
if (dependsOn != null) {
...
}
// 8.根据不同的scope作用域创建Bean
// CASE-SINGLETON:创建单实例Bean的逻辑(singleton模式的创建)
if (mbd.isSingleton()) {
...
}
// CASE-PROTOTYPE: 创建多实例Bean的逻辑(prototype模式的创建)
else if (mbd.isPrototype()) {
// prototype原型模式的创建(new)
.....
}
// CASE-OTHER:其他scope作用域创建Bean的逻辑,例如request、session、global session
else {
// 指定的scope上实例化bean
.....
}
catch (BeansException ex) {
...
}
finally {
beanCreation.end();
}
// 9.对最终得到的Bean实例进行类型转换
return adaptBeanInstance(name, beanInstance, requiredType);
}
}
我们跟进adaptBeanInstance(name, beanInstance, requiredType);
方法:
@SuppressWarnings("unchecked")
<T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) {
// 检查需要的类型是否符合Bean的实际类型
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
// 类型转换:
Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return (T) convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
经过以上的九个步骤,Bean 就算创建完成了(即,完整的doGetBean
方法的逻辑执行完毕)。其中第8步最为关键——针对不同的 scope 进行 Bean 的创建。
我们来大致总结一下一个 Bean 的创建过程(即,一个doGetBean
方法的逻辑):
成功加载 Bean 对象!
由于篇幅原因,将该文章内容分为(一)(二)两部分,请参考:Spring源码——Bean加载(二)
如果文章对大家有帮助,请三连支持一下!