Spring源码(四)-bean的加载(上)

前言

前面讲述了context的创建,接下来将进入到核心的bean的创建过程,前期的准备工作已经完成,相信很多人跟我一样,看过了一遍只能有个大概的印象,接下来有时间的话我会考虑结合UML和脑图这样的出来和大家一起分享,有经验或者想一起学习的,希望可以联系我,qq:616516146,言归正传,回到代码。

1、prepareContext()

接下来回到最初代码SpringApplication中run方法,我们先看prepareContext()这个方法。该方法是做context的准备工作。我们进入代码中看下他的具体实现。

    private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        /**
         * 该方法对context进行了预设置,设置了ResourceLoader和ClassLoader,
         * 并向bean工厂中添加了一个beanNameGenerator
         */

        postProcessApplicationContext(context);
        /**
         *
         * applyInitializers(context)方法
         * 获取到了我们或spring通过SpringApplication.setInitializers(xxx)
         * 设置的应用上下文初始化器集合
         */
        applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }

        // Add boot specific singleton beans
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }

        // Load the sources
        Set<Object> sources = getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        /**
         * 将各种bean装载进context对象中
         */
        load(context, sources.toArray(new Object[sources.size()]));
        listeners.contextLoaded(context);
    }


我们主要只讨论 postProcessApplicationContext(context)、 applyInitializers(context)以及 load(context, sources.toArray(new Object[sources.size()]))这三个方法

  • 1、postProcessApplicationContext(context)
    该方法对context进行了预设置,设置了ResourceLoader和ClassLoader, 并向bean工厂中添加了一个beanNameGenerator
  • 2、applyInitializers(context)
    该方法获取到了我们或spring通过SpringApplication.setInitializers(xxx)设置的应用上下文初始化器集合。
  • 3、load(context, sources.toArray(new Object[sources.size()]))
    这个方法主要是加载各种beans到context对象中的。sources代表各种资源对象,然后BeanDefinitionLoader的内部通过各种xxxReader和xxxScanner读取、解析这些资源对象中的beans
    /**
     * Load beans into the application context.
     * @param context the context to load beans into
     * @param sources the sources to load
     * 这个方法主要是加载各种beans到context对象中的。
     * sources代表各种资源对象,然后BeanDefinitionLoader
     * 的内部通过各种xxxReader和xxxScanner读取、解析这些资源对象中的beans。
     * 具体细节在    BeanDefinitionLoader中实现
     */
    protected void load(ApplicationContext context, Object[] sources) {
        logger.debug("开始执行load方法");
        System.out.println("-------------开始执行load方法");
        if (logger.isDebugEnabled()) {
            logger.debug(
                    "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        BeanDefinitionLoader loader = createBeanDefinitionLoader(
                getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
        loader.load();
    }

以上是load方法加载。
上面三个方法完成之后,prepareContext()函数已经准备好了refresh上下文的基础准备工作。,接下来看refresh的相关工作。

2、refresh

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            /**
             * 又注册了关闭context时的钩子
             */
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

这里我们进入refresh看看里面的具体实现,此处的refrsh调用的是AbstractApplicationContext的refrsh方法,之后又调用了context.registerShutdownHook();关闭了钩子。进入到AbstractApplicationContext,看下refrsh的具体实现,这里只截取了一部分代码

synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    prepareRefresh();
    // Tell the subclass to refresh the internal bean factory.
    //BeanFactory创建
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    // Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);
    try {
        // Allows post-processing of the bean factory in context subclasses.
        postProcessBeanFactory(beanFactory);
        // Invoke(调用) factory processors registered as beans in the context.
        //DefaultListableBeanFactory,完成basepackage的扫描
        invokeBeanFactoryPostProcessors(beanFactory);
        // Register bean processors that intercept bean creation.
        registerBeanPostProcessors(beanFactory);
        // Initialize message source for this context.
        initMessageSource();
        // Initialize event multicaster for this context.
        initApplicationEventMulticaster();
        // Initialize other special beans in specific context subclasses.
        onRefresh();
        // Check for listener beans and register them.
        registerListeners();
        // Instantiate all remaining (non-lazy-init) singletons.
        //DefaultListableBeanFactory
        //该方法进行了非懒加载beans的初始化工作
        finishBeanFactoryInitialization(beanFactory);
        // Last step: publish corresponding event.
        finishRefresh();
    }

这里我们只重点关注 finishBeanFactoryInitialization(beanFactory)这个方法。

    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }

        // Register a default embedded value resolver if no bean post-processor
        // (such as a PropertyPlaceholderConfigurer bean) registered any before:
        // at this point, primarily for resolution in annotation attribute values.
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
        }

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            logger.info("----------weaverAwareName:"+weaverAwareName);
            //创建bean的实例
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.
        //此处beanFactory是DefaultListableBeanFactory,获取bean
        beanFactory.preInstantiateSingletons();
    }

最后调用了beanFactory的preInstantiateSingletons方法,此处的beanFactory是DefaultListableBeanFactory。那么我们就看下这个的重点。

List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
    this.logger.info("-----beanName:"+beanName);
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        if (isFactoryBean(beanName)) {
            final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
                        ((SmartFactoryBean<?>) factory).isEagerInit(),
                        getAccessControlContext());
            }
            else {
                isEagerInit = (factory instanceof SmartFactoryBean &&
                        ((SmartFactoryBean<?>) factory).isEagerInit());
            }
            if (isEagerInit) {
                getBean(beanName);
            }
        }
        else {
            /**
             * 核心代码
             */
            getBean(beanName);
        }
    }
}

在这里看到了熟悉的getBean()方法,别的有兴趣和时间再研究吧,先忽略别的,仔细研究getBean()方法,经过追踪发现,他调用了AbstractoryFactory的doGetBean()方法,这里是一大段的代码。这里面有太多的代码,我们先简单描述,下节继续深入。

//实例化依赖的bean之后可以实例化mbd本身了
//单例模式的创建
if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
        try {
            /**
             *核心创建bean
             */
            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的实现,Spring默认的bean都是单例的,在此我们看到了createBean()方法,spring源码写的确实是比较优秀,根据方法名就能大致的知道其作用。这里的createBean()方法使用的是其子类的方法,这里摘取一点代码片段看下

/**
 * 锁定class,根据设置的class属性或者根据classname来解析class
 */
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
    mbdToUse = new RootBeanDefinition(mbd);
    mbdToUse.setBeanClass(resolvedClass);
}

到此bean加载和创建的基本流程也就结束了,下一篇我们着重会分析一些具体的细节。

代码的构建请参考 github
该地址有相应代码的注释

Spring源码分析

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

推荐阅读更多精彩内容