美文网首页Java 程序员Java
如何从 Spring IoC 容器中获取对象?

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

作者: 马小莫QAQ | 来源:发表于2021-03-02 21:47 被阅读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
    <?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> 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 对象,内容不多,后文再详细分解吧。

    作者:WriteOnRead
    链接:https://juejin.cn/post/6934495011971006472
    来源:掘金

    相关文章

      网友评论

        本文标题:如何从 Spring IoC 容器中获取对象?

        本文链接:https://www.haomeiwen.com/subject/metfqltx.html