Spring源码探究:IoC容器初始化过程详解

写在前面:我是直接通过阅读Spring源码并参考《Spring技术内幕(第2版)》一书来对Spring核心设计思想进行学习的。但是在学习的过程当中,我发现一个问题:书中为了能够用精简篇幅,采用了将部分父类中的代码放到子类当中的形式来进行讲解。这样会导致我们在查看某个类的时候发现并不存在书中提到的某些方法,这个时候就要灵活应用函数的调用栈以及类的继承关系回溯到该类的基类去查看某些方法。

在之前的博客Spring源码探究:IoC容器在Web容器中的初始化中,我已经介绍了IoC容器在Web容器中是如何被创建并进行初始化的。但是对初始化的介绍仅仅停留在文末对configureAndRefreshWebApplicationContext方法介绍的层面,并没有详细探究IoC容器初始化过程中涉及到的BeanDefinition的Resource定位、BeanDefinition的载入和解析以及BeanDefinition在IoC容器中的注册等部分。下面,我将参照Spring源码来对IoC容器初始化过程中涉及到的一些主要阶段进行介绍;

在炎热的天气下阅读大段大段的源码确实能算得上是一件令人十分心烦的事情了

在进一步探究之前,我们首先明确以下三点:

  • 下文中的“容器”如果没有特别说明的话,一律认为是"IoC容器";
  • 严格来说,容器的初始化过程主要是包括三个部分:BeanDefinition的Resource定位、BeanDefinition的载入和解析以及BeanDefinition在容器中的注册。至于容器的依赖注入,其实是发生在用户第一次向容器索要Bean时触发的,即用户调用getBean的时候将触发容器中依赖注入的过程。但是也有例外的时候,也就是我们可以在BeanDefinition中通过对lazy-init属性的设置来让容器完成对Bean的预实例化(其实也就是依赖注入的过程)。所以本文介绍的容器初始化过程中也会包含关于依赖注入的细节。
  • 由于我们探究的是IoC容器初始化的过程。在初始化的过程当中,我们会看到一个又一个的函数被调用。换句话说,其实是通过调用一些函数来完成IoC容器初始化的。因此,在本文中我会借助大量的函数调用栈(如图1.1.5、图1.1.7等等)来捋清整个过程当中发生了什么。如果你在阅读本文的过程中感觉思维混乱,请根据我所贴出的函数调用关系来阅读文章。

Spring源码探究:IoC容器在Web容器中的初始化文末提到了一个refresh()方法,这个方法其实标志着IoC容器初始化过程的正式启动。具体地来说,这个启动包括BeanDefinition的Resrouce定位、载入和注册三个基本过程。Spring之所以把这三个基本过程分开,并使用不同的模块来完成,如使用响应的ResourceLoader、BeanDefinitionReader等模块,通过这样的设计方式,可以让我们更加灵活地对这三个过程进行裁剪或拓展,定义出最适合自己的IoC容器的初始化过程。

第一部分 IoC容器的初始化过程

1. BeanDefinition的Resource定位

我们一直在说BeanDefinition,那么它到底是什么东西呢?
从字面上理解,它代表着Bean的定义。其实,它就是完整的描述了在Spring配置文件中定义的节点中所有信息,包括各种子节点。不太恰当地说,我们可以把它理解为配置文件中一个个<bean></bean>节点所包含的信息。

我们将以编程式使用DefaultListableBeanFactory来引入BeanDefinition的Resource定位
先介绍一下DefaultListableBeanFactory:

DefaultListableBeanFactory非常重要,是我们经常要用到的一个IoC容器的实现,比如在设计应用上下文ApplicationContext时就会用到它。这个DefaultListableBeanFactory实际上包含了基本IoC容器所具有的重要功能,也是在很多地方都会用到的容器系列中的一个基本产品。
在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。

图1.1.1 XmlBeanFactory就是在继承了DefaultListableBeanFactory容器功能的基础上增加了新的功能

编程式使用DefaultListableBeanFactory容器

ClassPathResource res = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinition(res);

如此,我们就可以通过factory对象来使用DefaultListableBeanFactory这个IoC容器了。
在使用IoC容器的时候,需要以下几个步骤:

  • 创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息,这里使用的是ClassPathResource
  • 创建一个BeanFactory,这里使用的是DefaultListableBeanFactory;
  • 创建一个载入BeanDefinition的读取器,这里使用的是XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,然后通过一个回调配置给BeanFactory。
  • 从定义好的资源位置读取配置信息,具体的解析过程由BeanDefinitionReader来完成。完成整个在如何注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。

从上面可以得知,以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息。

ClassPathResource resource = new ClassPathResource("bean.xml");

这里定义的BeanDefinition并不能由DefaultListableBeanFactory直接使用,Spring需要通过BeanDefinitionReader来对这些信息进行处理。

在这里,我们可以看到使用ApplicationContext相对于直接使用DefaultListableBeanFactory的好处。因为在ApplicationContext中,Spring已经为我们提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IoC容器,需要为它提供特定的读取器才能完成这些功能。但是从另一方面来讲,使用DefaultListableBeanFactory这种更底层的容器能提高定制IoC容器的灵活性。

比如说常用的一些ApplicationContext,例如FileSystemXmlApplicationContext、ClassPathXmlApplicationContext以及XmlWebApplicationContext等。从字面意思上我们就能看出它们可以提供哪些不同的Resource读入功能,比如FileSystemXmlApplicationContext可以从文件系统载入Resource,ClassPathXmlApplicationContext可以从Class Path载入Resource,XmlWebApplicationContext可以在Web容器中载入Resource等。

下面将以FileSystemXmlApplicationContext为例对IoC容器初始化过程进行探究

图1.1.2 FileSystemXmlApplicationContext中用于从文件系统中载入Resource的方法

图1.1.3 FileSystemXmlApplicationContext中含有refresh方法的构造函数
图1.1.4 我们可以发现,在FileSystemXmlApplicationContext中不管调用哪个构造函数,最终都是会调用这个包含了refresh方法的构造函数,因此我们可以很容易得出结论:触发对BeanDefinition资源定位过程的refresh方法的调用是在FileSystemXmlApplicationContext的构造函数中启动的。
图1.1.5 getResrouceByPath的调用关系栈

根据对上图调用关系以及FileSystemXmlApplicationContext中含有refresh方法的构造函数的分析,我们可以清楚地看到整个BeanDefinition资源定位的过程。这个对BeanDefinition资源定位的过程,最初是由refresh来触发的,这个refresh的调用是在FileSystemXmlApplicationContext的构造函数中启动的。

那么,FileSystemXmlApplication在什么地方定义了BeanDefinition的读入器BeanDefinitionReader,从而完成BeanDefinition信息的读入呢?在前面我们分析过,在IoC容器的初始化过程中,BeanDefinition资源的定位、载入和注册过程是分开进行的,这也是解耦的一个体现。关于这个读入器的配置,我们接下来到FileSystemXmlAppplicationContext的基类AbstractRefreshableApplicationContextrefreshBeanFactory方法去看看。

图1.1.6 AbstractRefershableApplicationContext中的refreshBeanFactory方法
图1.1.7

刚才我们提到,refresh的调用是在FileSystemXmlApplicationContext的构造函数中启动的,而这个refresh又是从AbstractApplicationContext继承过来的。因此,结合上面这个函数调用关系图我们知道:refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh调用。在这个方法(refreshBeanFactory)中,通过createBeanFactory构建了一个IoC容器供ApplicationContext使用。这个IoC容器就是前面我们提到的DefaultListableBeanFactory,同时它还启动了loadBeanDefinition来载入BeanDefinition,这里和以编程式使用IoC容器的过程很相似。

图1.1.8 在AbstractRefreshableApplictionContext中,这里是使用BeanDefinitionReader载入Bean定义的地方,因为允许有多种载入方式(虽然常用的是XML定义的形式),这里通过一个抽象函数把具体的实现委托给子类来完成

我们从图1.1.5的方法调用栈可以发现图1.6中refreshBeanFactory方法中调用的loadBeanDefinitions方法其实最终是调用AbstractBeanDefinitionReader里面的loadBeanDefinitions方法,下面我们直接看一下这个方法。

图1.1.9 AbstractBeanDefinitionReader中的loadBeanDefinitions方法

从上图可以得知,Resource的定位工作是由DefaultResourceLoader中的getResource来完成的。所以对于取得Resource的具体过程,我们来看看DefaultResourceLoader是怎样完成的:

图1.10 DefaultResourceLoader中的getResource方法

图1.1.10中使用的getResourcePath方法将会被FileSystemXmlApplicationContext实现,如图1.2所示。该方法返回的是一个FileSystemResource对象,通过这个对象,Spring可以进行相关的I/O操作,完成BeanDefinition的定位。

如果是其他的ApplicationContext,那么会对应生成其他种类的Resource,比如ClassPathResource、ServletContextResource等。

所以BeanDefinition的定位到这里就完成了。在BeanDefinition定位完成的基础上,就可以通过返回的Resource对象进行BeanDefinition的载入了。在定位过程完成之后,为BeanDefinition的载入创造了进行I/O操作的条件,但是具体的数据还没有开始读入。这些读入将在下面介绍的BeanDefinition的载入和解析中来完成。

2. BeanDefinition的载入和解析

在完成对BeanDefinition的Resource定位之后,我们现在来了解整个BeanDefinition信息的载入过程。对于IoC容器来说,载入过程相当于把定义的BeanDefinition在IoC容器中转化为一个Spring内部数据结构的过程。IoC容器对Bean的管理和依赖注入功能的实现是通过对其持有的BeanDefinition进行各种相关的操作来完成的。这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。需要注意的是,这只是一种比较简单的维护方式,如果需要提高IoC容器的性能和容量,可以自己做一些拓展。

以DefaultListableBeanFactory的设计入手来看看IoC容器是怎样完成BeanDefinition载入的

图1.2.1 启动BeanDefinition的载入

对于容器的启动来说,refresh是一个非常重要的方法。该方法在AbstractApplicationContext类(它是FileSystemXmlApplicationContext的基类)中找到,它详细地描述了整个ApplicationContext的初始化过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册等等。这个执行过程为Bean的生命周期提供了条件。

图1.2.2 refresh方法

3. BeanDefinition在IoC容器中的注册

BeanDefinition在IoC容器中完成了载入和解析的过程之后,用户定义的BeanDefinition信息已经在IoC容器内建立起了自己的数据结构以及相应的数据表示,但此时这些数据还不能供IoC容器直接使用,需要在IoC容器中对这些BeanDefinition数据进行注册。这个注册为IoC容器提供了更友好的使用方式,在DefaultListableBeanFactory中,是通过一个ConcurrentHashMap来持有载入的BeanDefinition的。

图1.2.3 DefaultListableBeanFactory中用于持有BeanDefinition的ConcurrentHashMap

将解析得到的BeanDefinition向IoC容器中的beanDefinitionMap注册的过程是在载入BeanDefinition完成后进行的。

BeanDefinition注册的实现

图1.2.4 用于BeanDefinition注册的registerBeanDefinition方法.png
图1.2.5 registerBeanDefinition方法的调用关系(是在XmlBeanDefinitionRead中的loadBeanDefinitions触发BeanDefinition注册的).png

完成了BeanDefinition的注册,就完成了整个IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,它们都在beanDefinitionMap里面被检索和使用/容器的作用就是对这些信息进行处理和维护。这些信息是容器建立依赖反转的基础,有了这些基础数据,我们下面学习一些在IoC容器中依赖注入是怎样完成的。

第二部分 IoC容器的依赖注入

在IoC容器初始化的过程里,主要完成的工作是在IoC容器中建立BeanDefinition数据映射,并没有看到IoC容器对Bean之间的依赖关系进行注入。

依赖注入主要发生在两个阶段

  • 正常情况下,依赖注入的过程是用户第一次向IoC容器索要Bean时触发的。
  • 但是我们可以在BeanDefinition信息中通过控制lazy-init属性来让容器完成对Bean的预实例化,即在初始化的过程中就完成某些Bean的依赖注入工作。

1.getBean触发的依赖注入

在基本的IoC容器接口BeanFactory中,有一个getBean的接口定义,这个接口的实现就是触发依赖注入发生的地方。为了进一步了解这个依赖注入的过程,我们从DefaultListableBeanFactory的基类AbstractBeanFactory入手去看看getBean的实现。

//这里是对BeanFactory接口的实现,比如说getBean方法
//这些getBean接口方法最终是通过调用doGetBean来实现的
@Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return doGetBean(name, requiredType, null, false);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, null, args, false);
    }

    /**
     * Return an instance, which may be shared or independent, of the specified bean.
     * @param name the name of the bean to retrieve
     * @param requiredType the required type of the bean to retrieve
     * @param args arguments to use when creating a bean instance using explicit arguments
     * (only applied when creating a new instance as opposed to retrieving an existing one)
     * @return an instance of the bean
     * @throws BeansException if the bean could not be created
     */
    public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
        return doGetBean(name, requiredType, args, false);
    }
        //这里是实际取得Bean的地方,也就是触发依赖注入的地方 
    protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

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

        // 先从缓存中取得Bean,处理那些已经被创建过的单例Bean,这种Bean不要重复创建
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            //这里的getObjectForBeanInstance完成的是FactoryBean的相关处理,以取得FactoryBean
           //的生产结果,BeanFactory和FactoryBean的区别我在后面会讲
            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);
            }

            // 检查IoC容器中的BeanDefinition是否存在,若在当前工厂不存在则去顺着双亲BeanFactory链一直向上找
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (args != null) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }

            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                 //根据Bean的名字取得BeanDefinition  
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // 递归获得当前Bean依赖的所有Bean(如果有的话)
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dependsOnBean : dependsOn) {
                        if (isDependent(beanName, dependsOnBean)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
                        }
                        registerDependentBean(dependsOnBean, beanName);
                        getBean(dependsOnBean);
                    }
                }

                // 通过调用createBean方法创建Singlton bean实例
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            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);
                }
                //这里是创建prototype bean的地方
                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 '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                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进行类型检查,如果没有问题,就返回这个新创建的Bean,这个Bean已经是包含了依赖关系的Bean
        if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
            try {
                return getTypeConverter().convertIfNecessary(bean, requiredType);
            }
            catch (TypeMismatchException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to convert bean '" + name + "' to required type [" +
                            ClassUtils.getQualifiedName(requiredType) + "]", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        return (T) bean;
    }

依赖注入就是在这里被触发的。而依赖注入的发生是在容器中的BeanDefinition数据已经建立好的前提下进行的。虽然我们可以用最简单的方式来描述IoC容器,那就是视其为一个HashMap,但只能说这个HashMap是容器的最基本的数据结构,而不是IoC容器的全部。

getBean是依赖注入的起点,之后会调用AbstractAutowireCapableBeanFactory中的createBean来生产需要的Bean,并且还对Bean初始化进行处理,比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等。

在这里对于依赖注入的所有细节就不一一介绍了,下面挑重点讲

依赖注入其实包括两个主要过程:

  • 生产Bea所包含的Java对象;
  • Bean对象生成之后,把这些Bean对象的依赖关系设置好。

与依赖注入关系特别密切的方法有createBeanInstancepopulateBean。前者用于生成Bean所包含的对象,后者主要是用来处理对各种Bean对象的属性进行处理的过程(即依赖关系处理的过程)。
所以下面我们针对这两个过程分别来学习。

生成Bean所包含的Java对象

在createBeanInstance中生成了Bean所包含的对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些生成方法都是由相关的BeanDefinition来指定的。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
        // 确认需要创建的Bean实例的类可以实例化
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
        }
        //这里使用工厂方法对Bean进行实例化
        if (mbd.getFactoryMethodName() != null)  {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }

        // Shortcut when re-creating the same bean...
        boolean resolved = false;
        boolean autowireNecessary = false;
        if (args == null) {
            synchronized (mbd.constructorArgumentLock) {
                if (mbd.resolvedConstructorOrFactoryMethod != null) {
                    resolved = true;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
        if (resolved) {
            if (autowireNecessary) {
                return autowireConstructor(beanName, mbd, null, null);
            }
            else {
                return instantiateBean(beanName, mbd);
            }
        }

        // 使用构造函数对Bean进行实例化
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
            return autowireConstructor(beanName, mbd, ctors, args);
        }

        // No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
    }
//最常见的实例化过程instantiateBean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    //使用默认的实例化策略对Bean进行实例化,默认的实例化策略是
       //CglibSubclassingInstantiationStrategy,也就是实用CGLIB来对Bean进行实例化    
    try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    @Override
                    public Object run() {
                        return getInstantiationStrategy().instantiate(mbd, beanName, parent);
                    }
                }, getAccessControlContext());
            }
            else {
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

这里使用了CGLIB对Bean进行实例化。CGLIB是一个字节码生成器的类库,它提供了一系列的API来提供生成和转换Java的字节码的功能。在Spring AOP中也使用CGLIB对Java的字节码进行增强。在IoC容器中,要了解怎样使用CGLIB来生成Bean对象,需要看一下SimpleInstantiationStrategy类。这个Strategy是Spring用来生成Bean对象的默认类,它提供了两种实例化Bean对象的方法:一种是通过BeanUtils,它使用了JVM的反射功能,另一种是通过CGLIB来生成。

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (bd.getMethodOverrides().isEmpty()) {
           //这里取得指定的构造器或者生成对象的工厂方法来对Bean进行实例化
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                                @Override
                                public Constructor<?> run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[]) null);
                                }
                            });
                        }
                        else {
                            constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Exception ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            //通过BeanUtils进行实例化,这个BeanUtils的实例化通过Constructor来实例化Bean
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // 使用CGLIB来实例化对象
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }
Bean之间依赖关系的处理

依赖关系处理的入口是前面提到的populateBean方法。由于其中涉及的面太多,在这里就不贴代码了。简要介绍一下依赖关系处理的流程:在populateBean方法中,首先取得在BeanDefinition中设置的property值,然后开始依赖注入的过程。首先处理autowire的注入,可以byName或者是byType,之后对属性进行注入。接着需要对Bean Reference进行解析,在对ManageList、ManageSet、ManageMap等进行解析完之后,就已经为依赖注入准备好了条件,这是真正把Bean对象设置到它所依赖的另一个Bean属性中去的地方,其中处理的属性是各种各样的。依赖注入发生在BeanWrapper的setPropertyValues中,具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现的,它会完成Bean的属性值的注入,其中包括对Array的注入、对List等集合类以及对非集合类的域进行注入。进过一系列的注入,这样就完成了对各种Bean属性的依赖注入过程。

在Bean的创建和对象依赖注入的过程中,需要依据BeanDefinition中的信息来递归地完成依赖注入。从前面的几个递归过程中可以看到,这些递归都是以getBean为入口的。一个递归是在上下文体系中查找需要的Bean和创建Bean的递归调用;另一个递归是在依赖注入时,通过递归调用容器的getBean方法,得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入。在对Bean的属性进行依赖注入时,解析的过程也是一个递归的过程。这样,根据依赖关系,一层层地完成Bean的创建和注入,直到最后完成当前Bean的创建。有了这个顶层Bean的创建和对它属性依赖注入的完成,意味着和当前Bean相关的整个依赖链的注入液完成了。

在Bean创建和依赖注入完成以后,在IoC容器中建立起一系列依靠依赖关系联系起来的Bean,这个Bean已经不再是简单的Java对象了。该Bean系列以及Bean之间的依赖关系建立完成之后,通过IoC的相关接口方法,就可以非常方便地供上层应用使用了。

2. lazy-init属性和预实例化

在图1.2.2的refresh方法中,我们可以看到调用了finishBeanFactoryInitialization来对配置了lazy-init的Bean进行处理。其实在这个方法中,封装了对lazy-init属性的处理,实际的处理是在DefaultListableBeanFactory这个基本容器的preInstantiateSingleton方法中完成的。该方法对单例Bean完成预实例化,这个预实例化的完成巧妙地委托给容器来实现。如果需要预实例化,那么就直接在这里采用getBean去触发依赖注入,与正常依赖注入的触发相比,只有触发的时间和场合不同。在这里,依赖注入发生在容器执行refresh的过程中,即IoC容器初始化的过程中,而不像一般的依赖注入一样发生在IoC容器初始化完成以后,第一次通过getBean想容器索要Bean的时候。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容