前面几篇文章主要分析了 Spring IoC 容器如何初始化,以及解析和注册我们定义的 bean 信息。

其中,「Spring 中的 IoC 容器」对 Spring 中的容器做了一个概述,「Spring IoC 容器初始化」和「Spring IoC 容器初始化(2)」分析了 Spring 如何初始化 IoC 容器,「Spring 是如何解析 标签的? 」分析了 Spring 如何解析 标签及其子标签,并注册到 BeanFactory。

主要流程如下:

IoC 容器已经建立,而且把我们定义的 bean 信息放入了容器,那么如何从容器中获取对象呢?

本文继续分析。

配置及测试代码

为便于查看,这里再贴一下 bean 配置文件和测试代码。

配置文件 application-ioc.xml

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd">

测试代码

public class IocTests {

@Test

public void test01() {

ApplicationContext context = new ClassPathXmlApplicationContext("application-ioc.xml");

System.out.println(context.getBean("person"));

System.out.println(context.getBean("dog"));

}

}

/*

* 输出结果:

* Person{id=12, name='Jack-12'}

* Dog{age=1}

*/

如何从容器获取对象?

从容器中获取对象是通过 BeanFactory#getBean 方法,它有多个重载的方法,但最终都是通过

AbstractBeanFactory#doGetBean 方法来实现的。doGetBean 方法代码如下:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

// ...

protected T doGetBean(

String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)

throws BeansException {

String beanName = transformedBeanName(name);

Object bean;

// 从缓存中获取单例 bean 对象

Object sharedInstance = getSingleton(beanName);

if (sharedInstance != null && args == null) {

// ...

// 处理 FactoryBean 的场景

bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

}

// 缓存中不存在 bean 对象

else {

if (isPrototypeCurrentlyInCreation(beanName)) {

throw new BeanCurrentlyInCreationException(beanName);

}

// bean 对象在父容器中,则从父容器中获取 bean 对象

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.

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);

}

}

// 是否只做类型检查

if (!typeCheckOnly) {

markBeanAsCreated(beanName);

}

try {

// 获取 BeanDefinition

RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

checkMergedBeanDefinition(mbd, beanName, args);

// 获取依赖的 bean 对象

// 若创建一个 bean 对象时依赖其他对象,则先创建被依赖对象

String[] dependsOn = mbd.getDependsOn();

if (dependsOn != null) {

for (String dep : dependsOn) {

if (isDependent(beanName, dep)) {

// ...

}

registerDependentBean(dep, beanName);

try {

getBean(dep);

}

catch (NoSuchBeanDefinitionException ex) {

// ...

}

}

}

// 创建 scope 为 singleton(单例)的对象

if (mbd.isSingleton()) {

sharedInstance = getSingleton(beanName, () -> {

try {

return createBean(beanName, mbd, args);

}

catch (BeansException ex) {

// ...

}

});

// 处理 FactoryBean 的场景

bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

}

// 创建 scope 为 prototype 的对象

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);

}

// 处理 FactoryBean 的场景

bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);

}

// 创建其他类型对象

else {

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);

}

});

// 处理 FactoryBean 的场景

bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);

}

catch (IllegalStateException ex) {

// ...

}

}

}

catch (BeansException ex) {

cleanupAfterBeanCreationFailure(beanName);

throw ex;

}

}

// 类型检查

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) {

// ...

}

}

return (T) bean;

}

}

获取 bean 对象主要就是通过这个 doGetBean 方法实现的。

该方法虽然看起来稍微有点长,但是呢,它内部的实现更长、更复杂。不过也是有迹可循的,莫慌。

本文先看下这个方法的整体流程,内部逻辑后面再慢慢研究。先上流程图:

代码虽然有点长,但梳理下来其实也没那么复杂了。

这个方法主要做了什么呢?

当从容器中获取 bean 对象时,首先从缓存中获取。如果缓存中存在,处理 FactoryBean 的场景。

BeanFactory 和 FactoryBean,这哥俩长得很像,也有个别面试题可能会问到。

嗯……以后有机会单独分析?

如果缓存中没有,先去父容器获取,前面创建 BeanFactory 时可以指定 parent 参数,就是那个。

不在父容器中,若 bean 对象依赖了其他对象,则先创建被依赖的 bean 对象,再根据 标签的 scope 属性去创建相应的 bean 对象。

是不是有点像我们平时写查询接口时、先从缓存查询,缓存中没的话再查询 DB?

道理是一样的,空间换时间。

小结

先整体,后细节。

本文先从整体上分析了如何从 Spring IoC 容器中获取 bean 对象,内容不多,后文再详细分解吧。

java 从一个容器获取对象,如何从 Spring IoC 容器中获取对象?相关推荐

  1. spring bean加载过程_Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

  2. Spring IOC 容器源码分析系列文章导读 1

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  3. Spring IoC容器与Bean管理

    Spring IoC容器与Bean管理 一.Spring IoC容器与Bean管理 1.Spring快速入门 IoC控制反转 DI依赖注入 Spring概述 Spring IoC初体验 使用XML方式 ...

  4. Spring IoC容器的初始化过程

    转载自:http://blog.csdn.net/u010723709/article/details/47046211 原题是:2 IOC容器初始化过程 作者:@小小旭GISer ========= ...

  5. Spring Ioc 源码分析(一)--Spring Ioc容器的加载

    1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...

  6. Spring IOC容器的依赖注入流程(收集和注册、分析和组装)

    Spring IOC容器的依赖注入流程 Spring IOC容器的依赖注入工作可以分为两个阶段: 阶段一:收集和注册 第一个阶段可以认为是构建和收集bean定义的阶段,在这个阶段中,我们可以通过XML ...

  7. Spring IOC容器和获取组件对象源码分析

    打上断点进行调试 1 第一步是进入了ClassPathXmlApplicationContext调用其构造参数,其中配置文件的内容被解析成了数组 public ClassPathXmlApplicat ...

  8. spring注入普通java类_普通java类如何取得注入spring Ioc容器的对象

    [除了使用XML配置外,还可以选择使用基于注解(annotation)的配置方式,其依赖于字节码来织入组件.注解注入在XML注入之前完成,因此在XML配置中可以重载注解注入的属性. 一.建一个Spri ...

  9. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

最新文章

  1. c语言模拟考试题目,10道C语言笔试模拟题
  2. NB驱动建立MQTT连接和断开MQTT连接的代码实现
  3. 黑盒之嵌入式操作系统鲁棒性研究
  4. html遮罩实例,给原生html中添加水印遮罩层的实现示例
  5. MySQL replace into (insert into 的增强版)
  6. Java,C实现约瑟夫环,一元多项式运算器
  7. 【Mac】mac安装redis客户端 Error: Cask ‘rdm‘ is unavailable: No Cask with this name exist
  8. Node Sass does not yet support your current environment解决
  9. Postman接口压力测试
  10. Novernber Rain
  11. ExtJS初学——renderTo/applyTo区别
  12. DTCloud编码规范
  13. canvas图片合成模糊变清晰的方法
  14. Matlab安装 解决error114
  15. 从单一服务到多元化服务,智能机器人JIMI的架构改造及逐步开放的过程
  16. 双头巨人 (twin)
  17. 原生php写简单的聊天室
  18. 《惢客创业日记》2021.08.28-31(周六)一错即否、一善俱荣(三)
  19. 毕业五年后,我决定再去读个名校计算机硕士学位
  20. php开发API接口的代码案例

热门文章

  1. apache karaf_未来是Apache Karaf上的微服务架构
  2. junit:junit_简而言之,JUnit:测试结构
  3. 使用Java 8.0进行类型安全的依赖注入
  4. EA问题的JDK14实例
  5. javafx中的tree_JavaFX中的塔防(6)
  6. 摆脱“空”检查的盛宴:使用JSON Patch正确执行PATCH
  7. 坚实原则:开放/封闭原则
  8. Spring Security和多个过滤器链
  9. qr码生成_从Java程序生成QR码图像
  10. 您的框架有多可扩展性?