Spring解析,加载及实例化Bean的顺序

在使用Spring时,Bean之间会有些依赖
比如一个BeanA实例化时需要用到BeanB,那么B应该在A之前实例化好。很多时候Spring智能地为我们做好了这些工作,但某些情况下可能不是,比如Springboot的@AutoConfigureAfter注解,手动的指定Bean的实例化顺序,了解Spring内Bean的解析,加载和实例化顺序机制有助于我们更好的使用Spring/Springboot,避免手动的去干预Bean的加载过程,搭建更优雅的框架。

Spring容器在实例化时会加载容器内所有非延迟加载的单例类型Bean,看如下源码:

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean {//刷新Spring容器,相当于初始化
    public void refresh throws BeansException,IllegalStateException {
        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();
                }
            }
        }
    }

    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
        /**
         * Listofbeandefinitionnames,inregistrationorder
         */
        private volatile List beanDefinitionNames = new ArrayList(256);

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

            List<String> 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); //实例化Bean
                }
            }
        }
    }
}

ApplicationContext内置一个BeanFactory对象,作为实际的Bean工厂,和Bean相关业务都交给BeanFactory去处理。
在BeanFactory实例化所有非延迟加载的单例Bean时,遍历beanDefinitionNames集合,按顺序实例化指定名称的Bean。beanDefinitionNames属性是Spring在加载BeanClass生成的BeanDefinition时,为这些Bean预先定义好的名称,看如下代码:

 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition)beanDefinition).validate();
            } catch (BeanDefinitionValidationException var8) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);
            }
        }

        BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            if (!this.isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }

            if (existingDefinition.getRole() < beanDefinition.getRole()) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
                }
            } else if (!beanDefinition.equals(existingDefinition)) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
                }
            } else if (this.logger.isTraceEnabled()) {
                this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
            }

            this.beanDefinitionMap.put(beanName, beanDefinition);
        } else {
            if (this.hasBeanCreationStarted()) {
                synchronized(this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    this.removeManualSingletonName(beanName);
                }
            } else {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                /**
                *BeanFactory在加载一个BeanDefinition(也就是加载BeanClass)时,
                *将相应的beanName存入beanDefinitionNames属性中,在加载完所有的BeanDefinition后,
                *执行Bean实例化工作,此时会依据beanDefinitionNames的顺序来有序实例化Bean,
                *也就是说Spring容器内Bean的加载和实例化是有顺序的,而且近似一致,当然仅是近似。
                */
                this.beanDefinitionNames.add(beanName);  
                this.removeManualSingletonName(beanName);
            }

            this.frozenBeanDefinitionNames = null;
        }

        if (existingDefinition == null && !this.containsSingleton(beanName)) {
            if (this.isConfigurationFrozen()) {
                this.clearByTypeCache();
            }
        } else {
            this.resetBeanDefinition(beanName);
        }

    }

BeanFactory在加载一个BeanDefinition(也就是加载BeanClass)时,将相应的beanName存入beanDefinitionNames属性中,在加载完所有的BeanDefinition后,执行Bean实例化工作,此时会依据beanDefinitionNames的顺序来有序实例化Bean,也就是说Spring容器内Bean的加载和实例化是有顺序的,而且近似一致,当然仅是近似。

Spring在初始化容器时,会先解析和加载所有的BeanClass,如果符合要求则通过Class生成BeanDefinition,存入BeanFactory中,在加载完所有BeanClass后,开始有序的通过BeanDefinition实例化Bean。

我们先看加载BeanClass过程,零配置下SpringBean的加载起始于ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)方法,我总结了下其加载解析BeanClass的流程:

Spring解析,加载及实例化Bean的顺序_第1张图片
配置类可以是Spring容器的起始配置类,也可以是通过 @ComponentScan 扫描得到的类,也可以是通过 @Import 引入的类。如果这个类上含有 @Configuration@Component@ComponentScan@Import@ImportResource注解中的一个,或者内部含有 @Bean 标识的方法,那么这个类就是一个配置类,Spring就会按照一定流程去解析这个类上的信息。

在解析的第一步会校验当前类是否已经被解析过了,如果是,那么需要按照一定的规则处理(@ComponentScan得到的Bean能覆盖@Import得到的Bean,@Bean定义的优先级最高)。

如果未解析过,那么开始解析:

解析内部类,查看内部类是否应该被定义成一个Bean,如果是,递归解析。

解析 @PropertySource ,也就是解析被引入的Properties文件。

解析配置类上是否有@ComponentScan注解,如果有则执行扫描动作,通过扫描得到的BeanClass会被立即解析成BeanDefinition,添加进beanDefinitionNames属性中。之后查看扫描到的BeanClass是否是一个配置类(大部分情况是,因为标识@Component注解),如果是则递归解析这个BeanClass。

解析@Import引入的类,如果这个类是一个配置类,则递归解析。

解析@Bean标识的方法,此种形式定义的BeanClass不会被递归解析

解析父类上的@ComponentScan,@Import,@Bean,父类不会被再次实例化,因为其子类能够做父类的工作,不需要额外的Bean了。

在1,3,4,6中都有递归操作,也就是在解析一个BeanClassA时,发现其上能够获取到其他BeanClassB信息,此时会递归的解析BeanClassB,在解析完BeanClassB后再接着解析BeanClassA,可能在解析B时能够获取到C,那么也会先解析C再解析B,就这样不断的递归解析。

在第3步中,通过@ComponentScan扫描直接得到的BeanClass会被立即加载入beanDefinitionNames中,但@Import和@Bean形式定义的BeanClass则不会,也就是说正常情况下面@ComponentScan直接得到的Bean其实例化时机比其他两种形式的要早。

通过@Bean和@Import形式定义的BeanClass不会立即加载,他们会被放入一个ConfigurationClass类中,然后按照解析的顺序有序排列,就是图片上的“将配置类有序排列”。一个ConfigurationClass代表一个配置类,这个类可能是被@ComponentScan扫描到的,则此类已经被加载过了;也可能是被@Import引入的,则此类还未被加载;此类中可能含有@Bean标识的方法。

Spring在解析完了所有BeanClass后,开始加载ConfigurationClass。如果这个ConfigurationClass是被Import的,也就是说在加载@ComponentScan时其未被加载,那么此时加载ConfigurationClass代表的BeanClass。然后加载ConfigurationClass内的@Bean方法。

顺序总结:@ComponentScan>@Import>@Bean

下面看实际的启动流程:
Spring解析,加载及实例化Bean的顺序_第2张图片
BeanClass的结构图如上所示,A是配置类的入口,通过A能直接或间接的引入一个模块。

此时启动Spring容器,将A引入容器内。

如果A是通过@ComponentScan扫描到的,那么此时的加载顺序是:

如果A是通过@Import形式引入的,那么此时的加载顺讯是:

当然以上仅仅代表着加载BeanClass的顺序,实际实例化Bean的顺序和加载顺序大体相同,但还是会有一些差别。

Spring在通过getBean(beanName)形式实例化Bean时,会通过BeanDefinition去生成Bean对象。在这个过程中,如果BeanDefinition的DependsOn不为空,从字面理解就是依赖某个什么,其值一般是某个或多个beanName,也就是说依赖于其他Bean,此时Spring会将DependsOn指定的这些名称的Bean先实例化,也就是先调用getBean(dependsOn)方法。我们可以通过在BeanClass或者@Bean的方法上标识@DependsOn注解,来指定当前Bean实例化时需要触发哪些Bean的提前实例化。

当一个BeanA内部通过@Autowired或者@Resource注入BeanB,那么在实例化A时会触发B的提前实例化,此时会注册A>B的dependsOn依赖关系,实质和@DependsOn一样,这个是Spring自动为我们处理好的。

了解SpringBean的解析,加载及实例化的顺序机制能够加深对Spring的理解,搭建更优雅简介的Spring框架。

你可能感兴趣的:(spring,java,spring,boot)