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

共 8037字,需浏览 17分钟

 ·

2021-03-04 20:27

前情回顾

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

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

主要流程如下:

89734b9aaf7fb1cc60c2c44f5a5626fb.webp

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

本文继续分析。

配置及测试代码

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

  • 配置文件 application-ioc.xml
<?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>
  • 测试代码
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<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 方法实现的。

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

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

d628aefc99b66153cfef23a5ef3e74f3.webp

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

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

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

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

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

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

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

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

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

小结

先整体,后细节。

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

休息会~

浏览 17
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报