通过《Spring读书笔记——bean加载》和《Spring读书笔记——bean解析》,我们明白了两件事。

  • Spring如何加载消化一个xml配置文件
  • Spring如何将xml文件的各种标签转换为BeanDefinition并注册到Spring容器下

    现在,我们理所当然的还差bean是如何被创建出来这一环节了。

从getBean说起

我们经常使用下面的方式实现先加载xml文件,然后获取相应的bean实例

BeanFactory beanFactory = new ClassPathXmlApplicationContext("application-context.xml");
TestBean testBean = beanFactory.getBean("testBean");

显然,我们是通过getBean方法获取到的Bean实例,该方法是接口BeanFactory中定义的一个方法。具体实现在另外一个抽象类中,就是我们一会要说到的AbstractBeanFactory。

AbstractBeanFactory

该抽象类集成了FactoryBeanRegistrySupport并实现了ConfigurableBeanFactory接口(该接口间接实现了接口BeanFactory)

通过上面的区块注释以及提供的方法getBean,我们一眼就看出其余BeanFactory的密切关系。

getBean

该方法非常简单,只是调用了一个函数,真正的实现都在doGetBean方法中了。代码实现有点长,但是我们还是得静下心来看看他到底做了哪些工作。

protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException { final String beanName = transformedBeanName(name);
Object bean; // Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
} // Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
} if (!typeCheckOnly) {
markBeanAsCreated(beanName);
} final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
} // Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
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 = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
// 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 {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
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);
}
}
}

归一化beanName

如果看了前面两篇文章,这里你就知道beanName就是注册到Spring容器中的bean的名称,具体来说就是放入BeanDefinitionMap中的一个键值对的key。

那么这里有什么好转换处理的呢。

我们看下transformedBeanName的具体实现

public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}

没错,做了一个断言处理,通过对于一种FactoryBean形式的bean做了处理。从FactoryBean这个命名就知道他是一种bean(不是病),有关FactoryBean的介绍以及使用可以参看博文:http://blog.csdn.net/is_zhoufeng/article/details/38422549,区别于BeanFactory(他是一个bean工厂,用于生产bean)。FactoryBean这种在注入bean的时候会在beanName前添加一个"&"修饰符,所以这里需要做归一化处理。

尝试从缓存中加载单例bean

Object sharedInstance = getSingleton(beanName);

该方法就是实现从缓存中获取单例bean。

对于单例,应该都不陌生,单例bean,单例模式等等说的都是一个意思——一个东西只有一份。

  • Spring默认创建的bean就是单例bean,也就是在容器中只会存在一份这样的bean
  • 这只是一次尝试加载,如果加载不到,通过后面的代码,我们可以发现其会从singletonFactories中加载
  • 加载单例bean可能会遇到一个头疼的问题——循环依赖,就是加载A时,A依赖了B,那么需要加载B,发现B依赖了C,这时候去加载C,发现C依赖了A,这样就形成了一个闭环,也就是循环依赖A->B->C-A。后面会说Spring是如何解决这个问题的
  • 有关Spring中不同类型的bean的循环依赖问题和解决方法可以参看https://my.oschina.net/yibuliushen/blog/737640

bean实例化

假设我们从缓存中得到了bean,但是这还不是bean的最终状态,可以认为这只是一个引用,要获得真正的bean实例,我们还需要看下getObjectForBeanInstance方法。

原型模式的循环依赖检查

这里引用下上面提供有关循环依赖的链接中比较重要的内容

spring循环依赖的情况
1.构造器注入属性依赖(A B两个对象都使用构造方法,注入依赖的属性)
无论是单例,还是原型对象,只要是通过构造器注入的属性依赖,都会报错,循环依赖错误 org.springframework.beans.factory.BeanCurrentlyInCreationException:
原因:试想,构造器是创建对象的入口方法,构造的时候都循环依赖了,我这个对象压根就创建不了啊。那肯定是无法解决的,大罗神仙也无能为力。
2.setter方法注入属性依赖
这个spring完美解决了,支持这种循环依赖
原理:创建对象A的时候,先通过无参构造方法创建一个实例,此时属性都是空的,但是对象引用已经创建出来,然后把A的引用提前暴露出来。然后setter B属性的时候,创建B对象,此时同样通过无参构造方法构造然后将对象引用暴露出来。接着B执行setter方法,去池中找A,能找到A(因为此时A已经暴露出来,有指向改对象的引用了),这么依赖B就构造完成,也初始化完成,然后A接着初始化完成。---循环依赖就这么解决了
3.原型对象的属性依赖(当然指的是通过setter方法注入依赖)
这个spring也无能为力,因为是原型对象,A创建的时候不会提前暴露出来,所以,每次都是要创建,创建的时候,发现有相同的对象正在创建,同样报错,循环依赖错误,同第一种情况类似。

我们知道对于单例默认的循环依赖,我们是可以解决的,但是对于原型类型的循环依赖,我们没有办法解决,所以这里通过对于原型bean的检查适时抛出异常。

if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}

检测parentBeanFactory

顺着代码的逻辑,很自然的来到了从parentBeanFactory中加载Bean的模块。当在缓存中没有加载到Bean的时候,我们就会从parentBeanFactory中试试。

转换为RootBeanDefinition

前两篇,我们介绍了从xml标签到BeanDefinition的转变,这里我们需要将GenericBeanDefinition转为RootBeanDefinition,这是处于后面的处理是一致对于RootBeanDefinition处理考量的。

解决bean依赖的问题

// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}

从注释就可以知道,这块主要是解决在初始化一个bean的时候,这个bean依赖了其他的bean,所以需要在创建之前先初始化依赖的bean。

对于scope的处理以及对于类型转换的处理

后面剩下的代码主要是对于Spring中不同scope的处理,比如singleton、prototype、request、session等等。

上面就是getBean这一抽象层次上关于如何创建bean的详细过程,下面对于其中一些部分做详细解释。

等等,写着写着发现后面的点还是有点多,一篇恐怕撑不住了,还是分上下集吧~~~

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

Spring读书笔记——bean创建(上)的更多相关文章

  1. Spring读书笔记——bean创建(下)

    有关Spring加载bean系列,今天这是最后一篇了,主要接上篇对于从Spring容器中获取Bean的一些细节实现的补充. <Spring读书笔记--bean加载>--Spring如何加载 ...

  2. Spring读书笔记——bean解析

    前情回顾 上篇<Spring读书笔记--bean加载>我们从代码角度介绍了有哪些类负责解析XML文件,又是如何一步步从XML格式脱变成我们熟悉的bean的,直到DefaultBeanDef ...

  3. Spring读书笔记——bean加载

    我们的日常开发几乎离不开Spring,他为我们的开发带来了很大的便捷,那么Spring框架是如何做到方便他人的呢.今天就来说说bean如何被加载加载. 我们在xml文件中写过太多类似这样的bean声明 ...

  4. (转) Spring读书笔记-----Spring的Bean之配置依赖

    前一篇博客介绍了Spring中的Bean的基本概念和作用域(Spring读书笔记-----Spring的Bean之Bean的基本概念),现在介绍Spring Bean的基本配置. 从开始我们知道Jav ...

  5. Spring读书笔记-----Spring的Bean之Bean的基本概念

    从前面我们知道Spring其实就是一个大型的工厂,而Spring容器中的Bean就是该工厂的产品.对于Spring容器能够生产那些产品,则取决于配置文件中配置. 对于我们而言,我们使用Spring框架 ...

  6. (转)Spring读书笔记-----Spring的Bean之Bean的基本概念

    从前面我们知道Spring其实就是一个大型的工厂,而Spring容器中的Bean就是该工厂的产品.对于Spring容器能够生产那些产品,则取决于配置文件中配置. 对于我们而言,我们使用Spring框架 ...

  7. Spring读书笔记-----Spring的Bean之设置Bean值

    [java] view plaincopyprint? Java实例的属性值可以有很多种数据类型.基本类型值.字符串类型.java实例甚至其他的Bean实例.java集合.数组等.所以Spring允许 ...

  8. Spring 读书笔记-----使用Spring容器(一)

    pring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口.他们都可代表Spring容器,Spri ...

  9. (转)Spring 读书笔记-----使用Spring容器(一)

    Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口.他们都可代表Spring容器,Spr ...

随机推荐

  1. 201521123097《Java程序设计》第八周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 1.本次作业题集集合 public static List return str; } pub ...

  2. 201521123114 《Java程序设计》第6周学习总结

    1. 本章学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 2. 书面作业 Q1.clone方法 1.1 Object ...

  3. 201521123106《java程序设计》第三周学习总结

    1. 本周学习总结 2. 书面作业 代码阅读 public class Test1 { private int i = 1;//这行不能修改 private static int j = 2; pub ...

  4. 201521123012 《Java程序设计》第二周学习总结

    1. 本章学习总结 1.Java中String和StringBuilder的区别. 2.Arrays()的用法. 2.课后作业 1.使用Eclipse关联jdk源代码(截图),并查看String对象的 ...

  5. 201521123034《Java程序设计》第十二周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象(属性:int id, String name,int age,doubl ...

  6. 201521123022 《Java程序设计》 第十周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 Q1.finally 题目4-2 Q1.1 截图你的提交结果 ...

  7. 201521123020《java程序设计》第十周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 finally 题目4-2 1.1 截图你的提交结果(出现学 ...

  8. [js高手之路]Node.js+jade抓取博客所有文章生成静态html文件

    这个周末,恶补了一下jade模板引擎,就为生成静态html文件,这篇文章需要知道jade以及看过我的上篇文章,我先给出他们的参考链接: [js高手之路]Node.js模板引擎教程-jade速学与实战1 ...

  9. 基于socket.io的实时在线选座系统

    基于socket.io的实时在线选座系统(demo) 前言 前段时间公司做一个关于剧院的项目,遇到了这样一种情况. 在高并发多用户同时选座的情况下,假设A用户进入选座页面,正在选择座位,此时还没有提交 ...

  10. Linux-exec命令试验驱动(12)

    对于做驱动经常会使用exec来试验驱动,通过exec将-sh进程下的描述符指向我们的驱动,来实现调试 -sh进程常用描述符号: 0:标准输入 1:标准输出 2:错误信息 5:中断服务 exec命令使用 ...