Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么

今天学习一下ApplicationContext在创建对象时,以及使用getBean方法时都干了什么?

准备简单的测试类代码:

    @Test
    public void test1(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        AccountService accountService = ac.getBean("accountService", AccountService.class);
    }

        给第一行打上断点,看看在创建对象时spring做了什么

        刚进入源码时得到这么一个源码,很显然,这个源码方法是一个构造方法,方法中调用了其他构造方法,我们跟进查。

        进入含有三个参数的构造方法,我们得到如下源码。我们来观察一下这个源码,第一行显然是使用了父类构造器

         第二行出现了一个变量configLocations,从控制台可以看出这个变量就是我们在构造器中传入的配置文件地址字符串。这部顾名思义就是将传入的参数设置为对象属性。我们可以跟进查看一下setConfigLocations做了什么

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第1张图片

         以下是setConfigLocations方法的源码,我们看关键步骤:不论if条件成立与否,this.configLocations都被赋值,那么显然这个方法就是给配置文件地址属性赋值。

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第2张图片

        我们回到构造器方法中继续向下,发现if的判断条件是真,那么代码就会执行该对象的refresh方法。 这里我们心理应该有素了,我们在学习ioc容器创建单例或者多例对象时,当创建的对象时单例对象,那么在容器初始化后就会创建好对象。前面都是简单的赋值,那么根据配置文件创建对象的工作应该就是refresh方法所为,那么我们进入该方法瞧瞧究竟是怎么回事。

        refresh方法源码:

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

        该方法内部全部都是其他方法的调用,该方法是一个汇总方法。刚进入刚方法是一个锁,这个锁对象时startupshutdownmonitor,start up是开始,shut down是关闭,monitor是监视器,那么这个锁就是来监视容器的开启和关闭的,为什么这个锁需要监视容器的开启或者关闭呢?这么一想很快就得出了答案:保证容器只有一个。

        下面就是弄懂这么多方法中的核心方法是哪些。

        直接向下走,当走完第二行时,完成了BeanFactory对象的创建,在控制台查看一下对象属性,(哈哈哈一大堆我也看不懂,但是不要着急,我们抓有信息的看,也就是有小箭头可以打开的变量)有一个beanDefinitionNames,他是ArrayList类型,是个集合啊!属性名称是bean的默认名称,我们打开看,我的天,是spring容器的xml配置文件中的配置的bean id。那么此刻spring容器已经解析玩了xml配置文件,并且把配置文件信息存储到了beanFactory对象中的beanDefinitionNames属性里。向上看还有一个beanDefinitionMap,里面也是存放id和value,key值就是标签中的id值,而value值他是一个GenericBeanDefinition对象,对象中有诸如对象属性是否单例等一系列配置的值。

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第3张图片

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第4张图片

        后面就是进入try语句,观察发现try语句中有多个方法是传入了BeanFactory对象

try {
    //postprocess英文后置处理,所以应该是BeanFactory的后置处理方法
    this.postProcessBeanFactory(beanFactory);

    //invoke:调用,postProcess:后处理程序,所以应该是调用BeanFactory的后处理程序
    this.invokeBeanFactoryPostProcessors(beanFactory);

    //register:注册,注册后处理程序
    this.registerBeanPostProcessors(beanFactory);

    //初始化消息源,与国际开发有关
    this.initMessageSource();

    //event:时间,multicaster:多的各种的,初始化各种Application时间
    this.initApplicationEventMulticaster();

    //初始化其他特殊bean,该方法不重写的话是空方法,跟进发现方法没有任何内容。
    this.onRefresh();

    //注册监听器
    this.registerListeners();

    //Initialization:初始化。完成BeanFactory的初始化
    this.finishBeanFactoryInitialization(beanFactory);

    //
    this.finishRefresh();
}

        可以猜测一下,既然要创建对象,那么肯定得用到xml文件中配置的对象,所以真正创建对象的八成是传入带有xml配置文件信息的beanFactory的方法。翻译一下方法名字,猜测一下方法的功能,其中我们发现finishBeanFactoryInitialization方法名的意思是:完成BeanFactory的初始化,这么翻译好像不妥,因为BeanFactory我们已经初始化好了,还作为参数传入了这个方法。所以这个方法应该是使用BeanFactory完成对象的初始化。我们把这边方法都跳过一遍,发现当跳过finishBeanFactoryInitialization方法时,对象完成了创建,那么此时可以断定,该方法使用了BeanFactory完成了对象的创建。

finishBeanFactoryInitialization源码

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        if (beanFactory.containsBean("conversionService") && beanFactory.isTypeMatch("conversionService", ConversionService.class)) {
            beanFactory.setConversionService((ConversionService)beanFactory.getBean("conversionService", ConversionService.class));
        }

        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver((strVal) -> {
                return this.getEnvironment().resolvePlaceholders(strVal);
            });
        }

        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        String[] var3 = weaverAwareNames;
        int var4 = weaverAwareNames.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String weaverAwareName = var3[var5];
            this.getBean(weaverAwareName);
        }

        beanFactory.setTempClassLoader((ClassLoader)null);
        beanFactory.freezeConfiguration();
        beanFactory.preInstantiateSingletons();
    }

        进入finishBeanFactoryInitialization方法,第一个if读起来有点懵逼,但是if中的方法setConversionService,conversion:转换,setConversionService:设置转换服务。概括一下就是如果出现一个情况,那么设置转换服务。如此一来,我们发现这个不是什么重要的方法。那么继续向下。

        来到了第二个if,方法内部的代码看的还是一脸懵逼,但是这里面有一个return,那么这段代码就是如果遇到特殊情况,那么处理特殊情况并返回,所以也不是核心区域,第二个if跳过。

        再往后有一个weaverAwareNames,他是String[]类型字符串数组,但是赋值完毕后数组还是空的。

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第5张图片

        所以这段噶了,继续向下。

        最后的最后有一个preInstantiateSingletons方法,整个finishBeanFactoryInitialization完成了对象初始化,而此方法之前的代码都没有创建对象,在联合其他方法我们发现,初始化单实例对象,那么这个方法应该就是我们要找的方法。

preInstantiateSingletons方法

public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Pre-instantiating singletons in " + this);
        }

        List beanNames = new ArrayList(this.beanDefinitionNames);
        Iterator var2 = beanNames.iterator();

        while(true) {
            String beanName;
            Object bean;
            do {
                while(true) {
                    RootBeanDefinition bd;
                    do {
                        do {
                            do {
                                if (!var2.hasNext()) {
                                    var2 = beanNames.iterator();

                                    while(var2.hasNext()) {
                                        beanName = (String)var2.next();
                                        Object singletonInstance = this.getSingleton(beanName);
                                        if (singletonInstance instanceof SmartInitializingSingleton) {
                                            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
                                            if (System.getSecurityManager() != null) {
                                                AccessController.doPrivileged(() -> {
                                                    smartSingleton.afterSingletonsInstantiated();
                                                    return null;
                                                }, this.getAccessControlContext());
                                            } else {
                                                smartSingleton.afterSingletonsInstantiated();
                                            }
                                        }
                                    }

                                    return;
                                }

                                beanName = (String)var2.next();
                                bd = this.getMergedLocalBeanDefinition(beanName);
                            } while(bd.isAbstract());
                        } while(!bd.isSingleton());
                    } while(bd.isLazyInit());

                    if (this.isFactoryBean(beanName)) {
                        bean = this.getBean("&" + beanName);
                        break;
                    }

                    this.getBean(beanName);
                }
            } while(!(bean instanceof FactoryBean));

            FactoryBean factory = (FactoryBean)bean;
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                SmartFactoryBean var10000 = (SmartFactoryBean)factory;
                ((SmartFactoryBean)factory).getClass();
                isEagerInit = (Boolean)AccessController.doPrivileged(var10000::isEagerInit, this.getAccessControlContext());
            } else {
                isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
            }

            if (isEagerInit) {
                this.getBean(beanName);
            }
        }
    }

        前面的if特殊条件判断先跳过,看后面的关键名称beanNames,在控制台打开beanNames属性看看里面都存了一些什么东东。打开发现是xml配置文件中每个bean的id。该步将id存放于一个List集合中,后面var2是迭代器对象,很明显要通过名称经过迭代器遍历了。

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第6张图片

         后面的循环太多了,我数了一下一共有三个while循环和四个do while循环,可怕。先关上第一个while的大括号,发现后面没有代码了。

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第7张图片

         关闭第二个do while循环,发现也没有了后续内容,第二个判断条件用到了instanceof关键字(instanceof是Java的一个保留关键字,左边是对象,右边是类,返回类型是类型。它的具体作用是测试左边的对象是否是右边类或者该类的子类创建的实例对象,是,则返回true,否则返回false

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第8张图片

       观察三层循环循环判断条件,当bd的值不是抽象的是单例模式的不是懒汉类型的时候三个循环终止,然后后续代码拿到当前条件成立的对象对bean进行进一步操作。这三个约束条件与我们在xml配置文件中配置的bean标签中的某些属性类似,难道bd就是我们需要的对象吗?那么我们首先看一下,bd究竟是什么。打开三层do while。

        if (!var2.hasNext()) {
            var2 = beanNames.iterator();

            while (var2.hasNext()) {
                beanName = (String) var2.next();
                Object singletonInstance = this.getSingleton(beanName);
                if (singletonInstance instanceof SmartInitializingSingleton) {
                    SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                    if (System.getSecurityManager() != null) {
                        AccessController.doPrivileged(() -> {
                            smartSingleton.afterSingletonsInstantiated();
                            return null;
                        }, this.getAccessControlContext());
                    } else {
                        smartSingleton.afterSingletonsInstantiated();
                    }
                }
            }

            return;
        }

        beanName = (String) var2.next();
        bd = this.getMergedLocalBeanDefinition(beanName);

        好家伙,一个if(如果迭代器没有下一个元素了,那么才执行内部代码)给我省去一大段代码,这个迭代器就是bean名称list集合的迭代器,那么if内部代码可以直接跳过了,直接看最后两行,第一步将迭代器中的list集合对应的字符串赋值给beanName变量,第二步传入名称,通过getMergedLocalBeanDefinition方法给bd赋初值,那么这bd究竟是何方神圣呢?

        打开debug控制台,看看bd内部有什么。注意beanClass属性,内部有与对象创建的各种各样的信息,而且只有一个,那么bd的真面目已经揭晓了:存储bean对象信息的容器。

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第9张图片

        这个bd我们先搁一边,马上再来看,先看看if内部的return,这就直接告诉我们了,如果是最后一个元素的话,那么就直接处理,不需要后继处理,处理完后直接返回,结束该方法,同时这个return也是大循环的终止条件。那么处理最后一个元素的方法与三层循环外的后继处理方法应该大相径庭。

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第10张图片

 

         后面出现了令人行动的方法:getBean

        这个命名可就太明显了,跟进看看,该方法究竟返回了什么。

        尴尬了兄弟们,这里的bean就是配置文件中的对象,只不过我的项目是使用过事务提交aop增强之后的项目,所以这里的bean是目标对象的Proxy代理对象了。但是不影响最终结果,那么就是getBean方法实实在在的创建了bean对象。

Spring的IOC容器源码学习——创建ApplicationContext时spring做了什么_第11张图片

         此时就先告一段落吧,毕竟内容有点多有点乱,先消化消化。经过本轮的学习,我们了解到getbean是spring容器创建对象的核心方法,ApplicationContext在创建对象的过程中,使用构造器的时候就通过传入的配置文件的路径,解析了配置文件,并把有关信息存放在了BeanFactory对象中,后续多处传入BeanFactory进行一系列操作包括最重要的创建对象。而对象的创建不可少的用到了BeanFactory中的map集合,即存储了id与bean对象设置其他内容的键值对。而对象的创建需要三层条件,都满足时创建对象,存放到ioc容器中,这里使用的方法时getBean,但是此方法没有返回值,那么我们可以做一个猜测,ioc容器是一个对象的某个集合类型的属性,此方法没有返回值,就是将创建好的对象直接放到了容器中,具体的我们明天在继续学习。

你可能感兴趣的:(Spring,IOC,源码解析,spring,容器,学习,ioc,java)