查看原文
其他

面试官问Spring中Bean如何完成加载,你准备怎么回答之上集?

SpringForAll社区 SpringForAll社区 2021-05-26

前言

前面讲述了context的创建,接下来将进入到核心的bean的创建过程,前期的准备工作已经完成,相信很多人跟我一样,看过了一遍只能有个大概的印象,接下来有时间的话我会考虑结合UML和脑图这样的出来和大家一起分享。

1、prepareContext()

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

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

9
10    postProcessApplicationContext(context);
11    /**
12     *
13     * applyInitializers(context)方法
14     * 获取到了我们或spring通过SpringApplication.setInitializers(xxx)
15     * 设置的应用上下文初始化器集合
16     */

17    applyInitializers(context);
18    listeners.contextPrepared(context);
19    if (this.logStartupInfo) {
20        logStartupInfo(context.getParent() == null);
21        logStartupProfileInfo(context);
22    }
23
24    // Add boot specific singleton beans
25    context.getBeanFactory().registerSingleton("springApplicationArguments",
26            applicationArguments);
27    if (printedBanner != null) {
28        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
29    }
30
31    // Load the sources
32    Set<Object> sources = getSources();
33    Assert.notEmpty(sources, "Sources must not be empty");
34    /**
35     * 将各种bean装载进context对象中
36     */

37    load(context, sources.toArray(new Object[sources.size()]));
38    listeners.contextLoaded(context);
39}

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

  • 1.postProcessApplicationContext(context)
    该方法对context进行了预设置,设置了ResourceLoaderClassLoader, 并向bean工厂中添加了一个beanNameGenerator

  • 2.applyInitializers(context)
    该方法获取到了我们或spring通过SpringApplication.setInitializers(xxx)设置的应用上下文初始化器集合。

  • 3.load(context, sources.toArray(new Object[sources.size()]))

    这个方法主要是加载各种beanscontext对象中的。sources代表各种资源对象,然后BeanDefinitionLoader的内部通过各种xxxReaderxxxScanner读取、解析这些资源对象中的beans

1  /**
2   * Load beans into the application context.
3   * @param context the context to load beans into
4   * @param sources the sources to load
5   * 这个方法主要是加载各种beans到context对象中的。
6   * sources代表各种资源对象,然后BeanDefinitionLoader
7   * 的内部通过各种xxxReader和xxxScanner读取、解析这些资源对象中的beans。
8   * 具体细节在    BeanDefinitionLoader中实现
9   */

10  protected void load(ApplicationContext context, Object[] sources) {
11      logger.debug("开始执行load方法");
12      System.out.println("-------------开始执行load方法");
13      if (logger.isDebugEnabled()) {
14          logger.debug(
15                  "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
16      }
17      BeanDefinitionLoader loader = createBeanDefinitionLoader(
18              getBeanDefinitionRegistry(context), sources);
19      if (this.beanNameGenerator != null) {
20          loader.setBeanNameGenerator(this.beanNameGenerator);
21      }
22      if (this.resourceLoader != null) {
23          loader.setResourceLoader(this.resourceLoader);
24      }
25      if (this.environment != null) {
26          loader.setEnvironment(this.environment);
27      }
28      loader.load();
29  }

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

2、refresh

1private void refreshContext(ConfigurableApplicationContext context) {
2    refresh(context);
3    if (this.registerShutdownHook) {
4        try {
5            /**
6             * 又注册了关闭context时的钩子
7             */

8            context.registerShutdownHook();
9        }
10        catch (AccessControlException ex) {
11            // Not allowed in some environments.
12        }
13    }
14}

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

1synchronized (this.startupShutdownMonitor) {
2    // Prepare this context for refreshing.
3    prepareRefresh();
4    // Tell the subclass to refresh the internal bean factory.
5    //BeanFactory创建
6    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
7    // Prepare the bean factory for use in this context.
8    prepareBeanFactory(beanFactory);
9    try {
10        // Allows post-processing of the bean factory in context subclasses.
11        postProcessBeanFactory(beanFactory);
12        // Invoke(调用) factory processors registered as beans in the context.
13        //DefaultListableBeanFactory,完成basepackage的扫描
14        invokeBeanFactoryPostProcessors(beanFactory);
15        // Register bean processors that intercept bean creation.
16        registerBeanPostProcessors(beanFactory);
17        // Initialize message source for this context.
18        initMessageSource();
19        // Initialize event multicaster for this context.
20        initApplicationEventMulticaster();
21        // Initialize other special beans in specific context subclasses.
22        onRefresh();
23        // Check for listener beans and register them.
24        registerListeners();
25        // Instantiate all remaining (non-lazy-init) singletons.
26        //DefaultListableBeanFactory
27        //该方法进行了非懒加载beans的初始化工作
28        finishBeanFactoryInitialization(beanFactory);
29        // Last step: publish corresponding event.
30        finishRefresh();
31    }

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

1protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
2    // Initialize conversion service for this context.
3    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
4            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
5        beanFactory.setConversionService(
6                beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
7    }
8
9    // Register a default embedded value resolver if no bean post-processor
10    // (such as a PropertyPlaceholderConfigurer bean) registered any before:
11    // at this point, primarily for resolution in annotation attribute values.
12    if (!beanFactory.hasEmbeddedValueResolver()) {
13        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
14    }
15
16    // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
17    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.classfalsefalse);
18    for (String weaverAwareName : weaverAwareNames) {
19        logger.info("----------weaverAwareName:"+weaverAwareName);
20        //创建bean的实例
21        getBean(weaverAwareName);
22    }
23
24    // Stop using the temporary ClassLoader for type matching.
25    beanFactory.setTempClassLoader(null);
26
27    // Allow for caching all bean definition metadata, not expecting further changes.
28    beanFactory.freezeConfiguration();
29
30    // Instantiate all remaining (non-lazy-init) singletons.
31    //此处beanFactory是DefaultListableBeanFactory,获取bean
32    beanFactory.preInstantiateSingletons();
33}

最后调用了beanFactorypreInstantiateSingletons方法,此处的beanFactoryDefaultListableBeanFactory。那么我们就看下这个的重点。

1List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
2// Trigger initialization of all non-lazy singleton beans...
3for (String beanName : beanNames) {
4    this.logger.info("-----beanName:"+beanName);
5    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
6    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
7        if (isFactoryBean(beanName)) {
8            final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
9            boolean isEagerInit;
10            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
11                isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
12                        ((SmartFactoryBean<?>) factory).isEagerInit(),
13                        getAccessControlContext());
14            }
15            else {
16                isEagerInit = (factory instanceof SmartFactoryBean &&
17                        ((SmartFactoryBean<?>) factory).isEagerInit());
18            }
19            if (isEagerInit) {
20                getBean(beanName);
21            }
22        }
23        else {
24            /**
25             * 核心代码
26             */

27            getBean(beanName);
28        }
29    }
30}

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

1//实例化依赖的bean之后可以实例化mbd本身了
2//单例模式的创建
3if (mbd.isSingleton()) {
4    sharedInstance = getSingleton(beanName, () -> {
5        try {
6            /**
7             *核心创建bean
8             */

9            return createBean(beanName, mbd, args);
10        }
11        catch (BeansException ex) {
12            // Explicitly remove instance from singleton cache: It might have been put there
13            // eagerly by the creation process, to allow for circular reference resolution.
14            // Also remove any beans that received a temporary reference to the bean.
15            destroySingleton(beanName);
16            throw ex;
17        }
18    });

这里是单例bean的实现,Spring默认的bean都是单例的,在此我们看到了createBean()方法,spring源码写的确实是比较优秀,根据方法名就能大致的知道其作用。这里的createBean()方法使用的是其子类的方法,这里摘取一点代码片段看下

1/**
2 * 锁定class,根据设置的class属性或者根据classname来解析class
3 */
4Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
5
6if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
7    mbdToUse = new RootBeanDefinition(mbd);
8    mbdToUse.setBeanClass(resolvedClass);
9}

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

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

Spring源码分析


限时扫码关注领取600页+Spring官方教程小书

  1. 限时3周免费下载

  2. 长按识别二维码关注SpringForAll社区公众号

  3. 在公众号回复“Spring”或者"spring"即可获取



推荐:人人都是 API 设计者:我对 RESTful API、GraphQL、RPC API 的思考

上一篇:Spring源码(三)-Context的创建核心逻辑(下)

点击原文阅读更多


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存