Spring IoC 源码系列(五)getBean 流程分析

一、FactoryBean 用法讲解

在分析源码流程之前,我们先来看一下 FactoryBean,乍一看这家伙和 BeanFactory 很像,它们都可以用来获取 bean 对象,简单来说 FactoryBean 是一种可以生产 bean 的 bean,而 FactoryBean 是一个生产 bean 的工厂。

下面举个例子来简单说明一下 BeanFactory 的用法:

    // 定义一个 User
    @Dat
    @Builder
    public class User {
        private String name;
        private Integer age;
    }

    // 定义一个 FactoryBean 用来创建 User 实
    public class UserFactoryBean implements FactoryBean<User> {
        @Override
        public User getObject() {
            return User.builder()
                    .name("jas")
                    .age(18)
                    .build();
        }
        @Override
        public Class<?> getObjectType() {
            return User.class;
        }
    }


    // 在 spring 的配置文件中配置 FactoryBean 实
    <bean id="userFactoryBean" class="com.jas.mess.factory.UserFactoryBean"/>

    // 编写测试类
    @Test
    public void factoryBeanTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
        System.out.println("userFactoryBean -> " + applicationContext.getBean("userFactoryBean", User.class));
        // & 可以用于获取 FactoryBean 本身
        UserFactoryBean userFactoryBean = applicationContext.getBean("&userFactoryBean", UserFactoryBean.class);
        System.out.println("&userFactoryBean -> " + userFactoryBean);
        System.out.println("userFactoryBean objectType -> " + userFactoryBean.getObjectType());
    }

输出结果如下:

从上面的输出结果中可以看出 FactoryBean 可以用来创建其他类型的 bean,如果想要获取 FactoryBean 实例本身,需要给 beanName 加上 & 前缀。

从 Spring IoC 容器获取 bean 的流程里有关于 FactoryBean 的处理,上面知道了 FactoryBean 的用法,下面一起来看下具体的流程细节吧!

二、getBean 流程分析

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        /**
         * 这里的 name 有三种情况:
         * 1.beanName 本身,即: 中 id 的值
         * 2.'&' 开头,表示获取 FactoryBean 本身
         * 3.alias 别名
         *
         * 如果 beanName 以 `&` 开头,则去除 `&` 前缀,如果是 alias 则替换为对应的 beanName
         */
        final String beanName = transformedBeanName(name);
        Object bean;

        // 单例模式的 Bean 在整个过程中只会被创建一次,第一次创建后会将该 Bean 加载到缓存中,再获取 Bean 就会从缓存中获取
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isTraceEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            /**
             * 注意这个 name 是没有经过处理的,有可能以 `&` 开头,即获取 FactoryBean 本身,而不是对应的 bean 实例
             * 当然如果是获取 FactoryBean 本身则会直接返回
             */
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        else {
            /**
             * 因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常
             * Spring 只能解决单例模式的循环依赖,为什么呢?因为单例模式下有对象缓存
             */
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

       		// 单例缓存中没有实例,则可能表明该实例还没有创建或者该实例在父容器中已经创建了,所以需要先检查一次父容器
            BeanFactory parentBeanFactory = getParentBeanFactory();
            // parentBeanFactory 不为空且 beanDefinitionMap 中已经保存过 beanName 对应的 BeanDefinition
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // 获取 name 对应的 beanName,如果 name 是以 & 字符开头,则返回 & + beanName
                String nameToLookup = originalBeanName(name);
                // 如果父类容器为 AbstractBeanFactory ,则调用 doGetBean 获取 bean
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                // 用明确的 args 从 parentBeanFactory 中,获取 Bean 对象
                else if (args != null) {
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                // 用明确的 requiredType 从 parentBeanFactory 中,获取 Bean 对象
                else if (requiredType != null) {
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                // 直接使用 beanName 从 parentBeanFactory 获取 Bean 对象
                else {
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }

            if (!typeCheckOnly) {
            	// 标记 bean 创建完成或将要创建,主要用来缓存
                markBeanAsCreated(beanName);
            }

            try {
                // 合并父 BeanDefinition 与子 BeanDefinition(父子容器根据 beanName 进行属性合并)
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                // 检查给定的合并的 beanDefinition
                checkMergedBeanDefinition(mbd, beanName, args);

                // 获取依赖的 bean
                String[] dependsOn = mbd.getDependsOn();
                /**
                 * 每个 Bean 都不一定是单独工作的,它可能会依赖其他 Bean,其他 Bean 也会依赖它
                 * 对于依赖的 Bean ,它会优先加载,所以,在 Spring 的加载顺序中,
                 * 在初始化某一个 Bean 的时候,首先会初始化这个 Bean 的依赖
                 *
                 * 注意这种依赖不是 bean 属性相互依赖,与我们常说的循环依赖是两种类型
                 *
                 *
                 * 
                 * 
                 *
                 * 对于上面这种依赖关系会抛出异常
                 */
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        // 如果存在循环依赖,则抛出异常
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        // 注入依赖的 bean 存储在对应的 map 中,记录彼此依赖的关系
                        registerDependentBean(dep, beanName);
                        try {
                            // 内部通过调用 doGetBean 方法 加载 depends-on 依赖的 bean
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }

                /**
                 * Spring Bean 的作用域默认为 singleton ,当然还有其他作用域,如 prototype、request、session 等
                 * 不同的作用域会有不同的初始化策略
                 * 如果是单例模式,因为刚开始是从单例缓存中获取,如果缓存中不存在,则需要从头开始加载
                 */
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    // 处理 FactoryBean 类型的 bean
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                // 原型模式
                else if (mbd.isPrototype()) {
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

                // 从指定的 scope 下创建 bean
                else {
                    String scopeName = mbd.getScope();
                    final 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);
                            }
                        });
                        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);
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        // 检查 bean 的实际类型是否符合需要的类型
        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) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Failed to convert bean '" + name + "' to required type '" +
                            ClassUtils.getQualifiedName(requiredType) + "'", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        return (T) bean;
    }

getBean 的主要流程就是上面这些,下面根据步骤来总结一下,对于一些比较重要的方法,下面会进行详细分析。

  1. 换 name 为 beanName,如果是别名,则循环处理获取最终指向的 beanName,如果以 & 开头,则剥离 & 前缀
  2. 根据 beanName 从单例缓存中获取 bean,如果 bean 不为空,则调用 getObjectForBeanInstance 方法
  3. 缓存中没有则获取父容器,父容器存在时尝试从父容器中获取 bean
  4. 父容器中没有该实例时,则合并父子的 BeanDefinition,接着处理 depends-on 的依赖关系
  5. 根据不同的类型创建 bean
  6. 创建 bean 时如果指定了类型,则对 bean 进行类型转换

下面对一些重要的方法单独拿出来分析,帮助大家理解。

2.1getSingleton

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

getSingleton 方法涉及到循环依赖问题,有兴趣的可以找下上篇文章,有详细解释。这里涉及到三个缓存 map,其中有一个就是用来解决循环依赖的,这里分别介绍一下。

  • singletonObjects:缓存实例化完成的单例 bean切属性被注入
  • earlySingletonObjects:缓存实例化完成,但还未注入属性的 bean,用于提前曝光 bean
  • singletonFactories:缓存初始化完成,属性未注入的 bean,当 bean 提前曝光(缓存到 earlySingletonObjects 中)以后,会删除该缓存,可以用来解决循环依赖

2.2 getObjectForBeanInstance

在一开始的时候我们先了解了 FactoryBean,下面的逻辑就会处理到。

    protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

        // 判断 nam 是否以 '&' 开头,如果是表示获取 FactoryBean 本身,而不是对应的 bean 实例
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            // 如果是 NullBean 直接返回
            if (beanInstance instanceof NullBean) {
                return beanInstance;
            }
            // 已 & 开头,如果不是 FactoryBean 类型抛出异常
            if (!(beanInstance instanceof FactoryBean)) {
                throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
            }
        }

        // 该实例可能是会是一个正常的 bean 又或者是一个 FactoryBean 本身,如果是则直接返回
        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }

        Object object = null;
        /**
         * 如果 beanDefinition 为 null,则从 factoryBeanObjectCache 缓存中获取 bean
         * FactoryBean 生成的单例 bean 会被缓存在 factoryBeanObjectCache 集合中,不用每次都创建
         */
        if (mbd == null) {
            object = getCachedObjectForFactoryBean(beanName);
        }
        // 如果代码执行这里则可以确认,beanInstance 一定是 FactoryBean 类型
        if (object == null) {
            // 转换成 FactoryBean 类型
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            if (mbd == null && containsBeanDefinition(beanName)) {
                // 将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition,
                // 合并 BeanDefinition
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            // 检测是用户定义的还是程序本身定义的
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            // 从 FactoryBean 中获取实例
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }
  1. 判断是否是 FactoryBean 类型,如果以 & 开头切不是 FactoryBean 类型抛出异常
  2. 如果是普通类型的 bean,获取要获取 BeanFactory 本身,则直接返回
  3. FactoryBean 中获取实例

getObjectFromFactoryBean 方法还有一些流程,这里就不赘述了,有兴趣的自己可以研究下。

参考

Spring IOC 容器源码分析 - 获取单例 bean by 田小波

你可能感兴趣的:(Spring,源码解析)