前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring 如何从 IoC 容器中获取对象?

Spring 如何从 IoC 容器中获取对象?

作者头像
WriteOnRead
发布于 2021-03-12 08:11:28
发布于 2021-03-12 08:11:28
11.6K00
代码可运行
举报
文章被收录于专栏:WriteOnReadWriteOnRead
运行总次数:0
代码可运行

前情回顾

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

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

主要流程如下:

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

本文继续分析。

配置及测试代码

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

  • 配置文件 application-ioc.xml
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">

    <bean id="person" class="com.jaxer.spring.ioc.Person">
        <property name="pet" ref="dog"/>
    </bean>

    <bean id="dog" class="com.jaxer.spring.ioc.Dog">
        <property name="age" value="1"/>
        <property name="owner" ref="person"/>
    </bean>

</beans>
  • 测试代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 方法代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    // ...

    protected <T> T doGetBean(
            String name, @Nullable Class<T> 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 对象,再根据 <bean> 标签的 scope 属性去创建相应的 bean 对象。

是不是有点像我们平时写查询接口时、先从缓存查询,缓存中没的话再查询 DB? 道理是一样的,空间换时间。

小结

先整体,后细节。

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WriteOnRead 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
LayUi的from组件搭配jQuery提交
layer是一款口碑极佳的web弹层组件,她具备全方位的解决方案,致力于服务各个水平段的开发人员,您的页面会轻松地拥有丰富而友好的操作体验。
凯哥Java
2019/06/28
1.2K0
Swoole跟thinkphp5结合开发WebSocket在线聊天通讯系统教程
这里的路径,是因为我绑定了home模块为默认模块,tp5默认情况是:php public/index.php index/Websocket/start)
猿哥
2019/03/29
1.9K0
Swoole跟thinkphp5结合开发WebSocket在线聊天通讯系统教程
layer弹框
官方介绍:layer至今仍作为layui的代表作,受众广泛并非偶然,而是这五年多的坚持,不断完善和维护、不断建设和提升社区服务,使得猿们纷纷自发传播,乃至于成为今天的Layui最强劲的源动力。目前,layer已成为国内最多人使用的web弹层组件,GitHub自然Stars3000+,官网累计下载量达30w+,大概有20万Web平台正在使用layer。
王小婷
2025/05/18
1400
layer弹框
LayUI树形表格treetable使用详解
LayUI是现在比较流行的一款前端框架,也有很多人基于LayUI开发了很多不错的组件,比如treetable树形表格。因为treetable是第三方基于LayUI开发的,所以需要先用Layui引入一下文件。
全栈程序员站长
2022/07/01
9.3K1
LayUI树形表格treetable使用详解
Layui的用途——用户登录界面为案例
官方网站:https://www.layui.com/(已下线)    参考地址:http://layui.org.cn/demo/index.html(非官网)
用户10196776
2022/11/22
2K0
Layui的用途——用户登录界面为案例
layer弹框在实际项目中的一些应用
官方介绍:layer至今仍作为layui的代表作,受众广泛并非偶然,而是这五年多的坚持,不断完善和维护、不断建设和提升社区服务,使得猿们纷纷自发传播,乃至于成为今天的Layui最强劲的源动力。目前,layer已成为国内最多人使用的web弹层组件,GitHub自然Stars3000+,官网累计下载量达30w+,大概有20万Web平台正在使用layer。 同时也高居年度最受欢迎的开源项目榜单:2017年度最流行的十大中国开源软件:https://www.jianshu.com/p/d7a76eee56e6 受到
王小婷
2018/05/31
1.6K0
Thinkphp6学习(8)layui-form异步提交和登陆后的页面跳转
登陆界面代码,放在index/view/index/testlogin.html文件中
哆哆Excel
2022/10/25
2K0
Thinkphp6学习(8)layui-form异步提交和登陆后的页面跳转
layui treeTable「建议收藏」
layui table结构不能很直观的展示层级信息,所以参考”https://fly.layui.com/extend/treeTable/“组件(layui版本为v2.5.6),修改为树形展示,修改了treeTable.js,保留了一些原table定义;
全栈程序员站长
2022/08/11
1.9K0
layui treeTable「建议收藏」
LayUI之旅-入门
最近要做一个项目,被要求前端要使用layui,甲方爸爸很牛逼的好吗,既然要求这样了,二话不说,撸起袖子就开干,由于从来没用过layui这个框架,对框架的不熟悉导致在使用的过程中是步步都是障碍啊,还是那句话“好记性不如烂笔头”,那就记录一下都遇到了些什么问题以及一些用法吧。
Yiiven
2022/12/15
3K0
layui的layer弹出层和form表单
如果想用layui来完成增删改查,那么要会用弹出层和form表单这两个组件是必须的,所以今天就来介绍一些如何用layui完成基本的增删改查
全栈程序员站长
2022/09/14
5.1K0
layui的layer弹出层和form表单
layui 树形表格 treeTable使用详细指南,不能折叠解决办法
最近在写一个商品分类管理的功能,本来想用layui的树形组件来写,但发现layui原生的tree只能展示title,而分类的其他字段无法展示,这就有点不适用了,无意中看到一位大神自定义写的一个树形表格组件,正好满足我的要求,特此将使用方法以及其中遇到的一些坑记录下来。。。
全栈程序员站长
2022/08/22
5.6K0
layui 树形表格 treeTable使用详细指南,不能折叠解决办法
如何通过IP地址获取用户所在城市?
在日常开发过程中,经常有通过IP去获取用户位置,或在服务器日志中查看到各种各样的ip地址,如何通过ip地址去获取用户的信息呢?比如所在城市,网络提供商是联通,移动,电信呢,通过接口我们可以实现这些功能。
申霖
2019/12/27
2.9K0
如何通过IP地址获取用户所在城市?
LayCenter简易适配文档
首先感谢您能适配本站开发的LayCenter(用户中心)插件,如果您是zblog的开发者,可以先购买本插件对主题进行适配。适配完成后,成功介绍1位用户购买本插件,您购买本插件的钱全额退还。
李洋博客
2023/10/13
3030
LayCenter简易适配文档
layui框架——弹出层layer[通俗易懂]
Array:例如title: [‘文本’, ‘font-size:18px;’],数组第二项可以写任意CSS样式
全栈程序员站长
2022/08/18
14.2K0
layui框架——弹出层layer[通俗易懂]
大文件上传代码片段记录
记录一下自己写的PHP大文件分段上传代码,方面以后要用的时候直接复制粘贴。使用了Layui、JQuery和ThinkPHP,还有一些优化空间,等下次用到的时候再完善~
jwj
2022/05/18
6340
快速使用layui和ssm开发
大概就这么多,本地导入static文件后,直接复制上面的index首页就可以开始进行layui开发了,很方便。
废江_小江
2022/09/05
6160
layui弹出层提交表单![通俗易懂]
特别声明:在父层提交表单,需要获取表单页面的数据,并且调用后台接口,如上代码中的yes:后面的代码,要注意,这里的layui教程中,yes后面的function参数顺序错误了应该是function(index,layero){}不是function(layero,index){},鄙人也是花了很久才弄明白这么回事。
全栈程序员站长
2022/08/23
4.7K0
【前端系列-2】layui+springboot实现表格增删改查
本文将演示如何使用Springboot(后端框架)和layui(前端框架)将数据库中的数据渲染到前端页面,以及对前端页面的数据实现增删改。
云深i不知处
2020/09/16
7K0
layui与VFP搭配完成单表增删查改,勇于尝试才好玩
由职业前端倾情打造,面向全层次的前后端开发者,易上手开源免费的 Web UI 组件库
加菲猫的VFP
2024/02/27
2140
layui与VFP搭配完成单表增删查改,勇于尝试才好玩
ThinkPHP5框架:Layui 下 image、video、excel 文件的上传实现
【提示】 这里的进度条上传是虚拟的哦,其实就是为了能提示一下没上传完就可以了,哈哈…
泥豆芽儿 MT
2020/05/25
1.6K0
ThinkPHP5框架:Layui 下 image、video、excel 文件的上传实现
相关推荐
LayUi的from组件搭配jQuery提交
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档