Spring Bean 的加载源码解析

作者: 奋小二 | 来源:发表于2018-09-02 16:43 被阅读123次

Spring 对bean的加载是Spring中比较重要的一个环节,逻辑也是比较复杂。下面选取几点来介绍Spring加载bean的相关过程。

  • getBean的流程
  • 单例获取bean
  • 准备创建bean
  • 循环依赖
  • 拓展接口

好了废话不多说,系好安全带,准备出发!

getBean的流程

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

        final String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.
        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 + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // Check if bean definition exists in this factory.
            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 {
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                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 + "'");
                        }
                        registerDependentBean(dep, beanName);
                        try {
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }

                // Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                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);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

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

        // Check if required type matches the type of the actual bean instance.
        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;
    }

这个是Spring获取Bean的整个方法,我们一点一点的来分析

  • 首先是这个final String beanName = transformedBeanName(name);
    好奇的人可能就会问了,为什么还要转换一下name呢,方法不是已经传进来了吗?其实不是,这个name可能是别名,也可能是FactoryBean,所以就需要进行一些解析,具体包括哪些内容呢:

    • 去除FactoryBean的修饰符,也就是如果name="&&abdc",那么会首先去除&
    • 取别名对应的beanName,例如别名A指向的其实是名称为B的bean,那么返回的就是B
  • 接下来是尝试从缓存中加载单例
    单例对象在Spring的同一个容器中只会被加载一次,后续的获取就直接从缓存中读取了。这里也只是尝试从缓存中获取,如果获取不成功,那么后面会再次从singletonFactories中加载。
    这里提前说明一下,因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建的时候需要依赖上一个bean的时候,就直接使用ObjectFactory,这个后面会详细解释。

  • bean的实例化
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    这个也是代码中出现频率比较高的方法,这里先简单描述一下为什么要有这个方法,因为从缓存中获取到的bean是处于原始状态,那么就需要对bean进行实例化,比如我们是从工厂中获取到的bean,那么就需要工厂bean中定义的factory-method方法返回的bean.这个方法也就是完成这个的。

从缓存中获取单例Bean

Object sharedInstance = getSingleton(beanName);
这行代码就是首先尝试从缓存中获取单例bean, 那么这个方法内部又做了些什么呢?

@Nullable
    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;
    }

代码逻辑都看的懂,这里主要说说其中的几个map对象都是做什么的

  • singletonObjects : 用于保存BeanName 和创建bean实例的关系,如果这里能找到,就直接返回了。
  • singletonFactory : 用于保存BeanName和创建工厂之间的关系的,就是bean name -----> ObjectFactory
  • earlySingletonObjects : 这个也是用于保存BeanName和创建bean的实例之间的关系的,与singletonFactory的不同之处在于,当一个单例的bean被放到这里后,那么当bean还在创建过程中,就可以通过getBean方法被引用到,目的就是用于解决循环引用的问题。
  • registeredSingleton :用于保存当前已经注册的bean。

这里啰嗦几句,isSingletonCurrentlyInCreation就是判断当前需要的bean是否已经正在创建当中,如果是正在创建当中,那么下面就通过earlySingletonObjects获取到对应的引用,完成当前bean的获取或者是初始化。其实在创建一个bean的之前提前将beanName维护到这里,在bean创建结束之后会在这其中清楚掉。目的就是解决循环依赖产生的无法创建成功的问题。

  • 从bean的实例中获取对象
    上面首先从缓存中获取到bean的实例后要做的第一件事就是调用getObjectForBeanInstance检测一下其正确性,就是检测当前bean是否是FactoryBean类型的bean,如果是就需要调用该bean对应的FactoryBean实例的getObject方法获取实际bean对象。
    这里无论是从缓存还是通过不同scope加载的bean都只是最原始的bean状态,不一定是我们最终想要的bean.举个例子,假如我们需要对工厂bean进行处理,那么这里得到的其实就是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean。这也是这个方法要做的事情。

准备创建Bean

先看段代码

    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        if (logger.isTraceEnabled()) {
            logger.trace("Creating instance of bean '" + beanName + "'");
        }
        RootBeanDefinition mbdToUse = mbd;

        // Make sure bean class is actually resolved at this point, and
        // clone the bean definition in case of a dynamically resolved Class
        // which cannot be stored in the shared merged bean definition.
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        // Prepare method overrides.
        try {
            mbdToUse.prepareMethodOverrides();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                    beanName, "Validation of method overrides failed", ex);
        }

        try {
            // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }

        try {
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isTraceEnabled()) {
                logger.trace("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
            // A previously detected exception with proper bean creation context already,
            // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
        }
    }
  • mbdToUse.prepareMethodOverrides();这里大家可能会有些疑惑,因为在Spring中没有override-method这样的属性,那么这里到底是做什么的呢?
    其实在Spring中虽然没有override-method这样的属性,但是在配置中是有lookup-method 和 replace-method,这里就是将这两个方法的配置添加到BeanDefinition中的methodOverrides的属性里。
    具体的lookup-method 和 replace-method使用这里就不赘述了,有兴趣的可以自行查阅。

循环依赖

本文最重要的一个问题来了,就是循环依赖。循环依赖就是两个或多个bean相互之间持有对方。那么在创建bean的时候,设计到依赖注入就会出现循环。如果不对这种情况进行处理,那么就可能出现死循环,最终OOM。

那么Spring中是怎么对待循环依赖的呢?
首先说明一下有几种循环依赖:

  • 构造器循环依赖
  • setter 循环依赖
    首先说明目前Spring是无法解决构造器的循环依赖的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖异常。

setter注入的方式循环依赖是通过Spring容器提前暴露刚完成的构造器注入但未完成其他步骤的bean来实现的。而且只能解决单例scope的的循环依赖

为什么对于scope为prototype的循环依赖呢?因为Spring在处理prototyped的bean的时候是不进行缓存的,因此无法提前暴露一个创建中的bean ,所以无法完成循环依赖的操作,最终也是通过抛出异常来提示出现了Spring无法处理的循环依赖

这里简单再说一些Spring创建bean对象的细节,其实按照我们自己的想象,如果想要创建一个对象,那么直接通过反射来构造一个就好了,但是Spring其实并没有这么做。为什么呢?

还记得上面说到lookup 和 overrider的方法吗?它们被存放到beanDefinition的MethodOverrides属性中。所有如果这里直接通过反射的方法构建对象,那么就没有办法完成特殊配置了。因为需要将这两个配置的功能切入进去。所以就必须通过动态代理的方式将包含这两个特性的逻辑进行拦截增强处理。这样才能保证在调用方法的时候被拦截器拦截处理。

拓展接口

Spring中提供了一些Aware相关接口。BeanFactoryAware,ApplicationContextAware等等。实现这些接口的bean在被初始化之后,可以取得一些相应的资源。实现BeanFactoryAware的bean,在bean被初始化之后,将会被注入BeanFactory的实例,可以用来进一步获取其他bean对象。

  • BeanPostProcessor
    这个是Spring开放框架中给用户权限取更改和拓展Spring。
    在调用客户自定义初始化方法前和调用自定义初始化方法之后分别会调用BeanPostProcessor的
    • postProcessorBeforeInitialization
    • postProcessorAfterInitialization
      使用户可以根据自己的业务需求进行相应处理。

客户定制化初始化的方法除了init-method之外,还可以通过让bean实现InitializingBean接口来实现,并在afterPropertiesSet()方法之后进行初始化的业务逻辑处理。

init-method与afterPropertiesSet都是在初始化bean的时候执行,执行顺序是afterPropertiesSet先执行,init-method后执行

相关文章

网友评论

    本文标题:Spring Bean 的加载源码解析

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