SpringBoot之容器啓動源碼分析與Bean加載

SpringBoot之啓動容器源碼分析

1、SpringApplication#run()

由SpringBoot應用引導類的 SpringApplication#run() 進入我們可以看到以下代碼

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        // 監視器啓動
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        // 從 META-INF/spring.factories 中獲取 SpringApplicationRunListener 實現類,並調用starting()
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 準備環境變量配置
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            // 通過 spring.beaninfo.ignore 配置是否忽略bean信息 暫不明覺厲
            this.configureIgnoreBeanInfo(environment);
            // 打印SpringBoot控制檯啓動圖案
            Banner printedBanner = this.printBanner(environment);
            // 創建應用上下文
            context = this.createApplicationContext();
            // 從 META-INF/spring.factories 中獲取異常報告處理器,用作處理啓動異常
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            /**
              * 準備應用上下文
              * 1 設置上下文環境配置
              * 2 註冊 beanNameGenerator 等
              * 3 執行 ApplicationContextInitializer#initialize()初始化
              */
            ApplicationContextInitializer#initialize() 初始化
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            
            // 刷新上下文(下面重點分析)
            this.refreshContext(context);
            
            // 容器初始化之後 開始執行 ApplicationRunner 和 CommandLineRunner 自定義初始化執行器
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

2、SpringBoot 啓動源碼之 refreshContext() 方法

通過上面的代碼可以看出,關鍵代碼在於
this.refreshContext(context)
該方法最終會執行
AbstractApplicationContext#refresh()
這纔是Spring容器啓動的核心內容,重頭戲一起往下看:

  • spring容器初始化總覽 AbstractApplicationContext#refresh()
public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
        	// 準備初始化容器工作,設置啓動標誌、記錄啓動時間等
            this.prepareRefresh();
            // 創建 beanFactory, 並加載bean定義等
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            // beanFactory注入一些標準組件,例如ApplicationContextAwareProcessor,ClassLoader等
            this.prepareBeanFactory(beanFactory);

            try {
            	// 給實現類留的一個鉤子,例如注入BeanPostProcessors,這裏是個空方法 
                this.postProcessBeanFactory(beanFactory);

                // 執行 BeanFactoryPostProcessor 實現類的 postProcessBeanFactory()方法
                this.invokeBeanFactoryPostProcessors(beanFactory);

                // 註冊 BeanPostProcessor 實現類
                this.registerBeanPostProcessors(beanFactory);
                
	            // 國際化資源處理 
                this.initMessageSource();

				// bean工廠註冊一個key爲applicationEventMulticaster的廣播器 用於事件廣播
                this.initApplicationEventMulticaster();

				// 給實現類留的一鉤子,可以執行其他refresh的工作,比如啓動tomcat server
                this.onRefresh();

				// 註冊事件監聽器
                this.registerListeners();
                
				// 完成bean的初始化
                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();
            }

        }
    }

上述即spring容器啓動流程總覽,不建議大家閱讀源碼每一行代碼都需要看懂,細看的話只會讓你陷入到無窮無盡的代碼海洋中。我們需要學會擇重避輕,下面我將重點分析bean初始化流程。

3、Spring bean的加載

由上述源碼我們可以看出bean的加載核心代碼在於
this.finishBeanFactoryInitialization(beanFactory);
下面一起來分析以下該方法的實現,以下直接上關鍵代碼:

bean的三級緩存,以及bean的初始化

/**
 * finishBeanFactoryInitialization() 最終會調用該方法
  */
 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();
                                        // 這裏是從 bean的三級緩存中取
                                        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的三級緩存中獲取bean,如緩存中沒有,則調用getBean()

Bean的三級緩存

  • singletonObjects:用於存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用

  • earlySingletonObjects:提前曝光的單例對象的cache,存放原始的 bean 對象(尚未填充屬性),用於解決循環依賴

  • singletonFactories:單例對象工廠的cache,存放 bean 工廠對象,用於解決循環依賴

	/**
	  * 從三級緩存中獲取bean
	  */
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            synchronized(this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }

        return singletonObject;
    }

下面看getBean() 方法調用如下

1、getBean()
2、doGetBean()
3、createBean()
4、doCreateBean()
5、populateBean()
6、initializeBean()
以上看出獲取bean還是很曲折的,我們直接關注最後 initializeBean()

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
 		// 執行 Aware 接口
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(() -> {
                this.invokeAwareMethods(beanName, bean);
                return null;
            }, this.getAccessControlContext());
        } else {
            this.invokeAwareMethods(beanName, bean);
        }
		/**
		  * 執行 BeanPostProcessor#postProcessBeforeInitialization         bean初始化前置方法
		  * 其中 InitDestroyAnnotationBeanPostProcessor 來執行 @PostConstruct 標註的方法
		  */
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        }

        try {
        	// bean初始化方法 init-method 和 InitializingBean#afterPropertiesSet()
            this.invokeInitMethods(beanName, wrappedBean, mbd);
        } catch (Throwable var6) {
            throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
        }
		
		/**
          * 執行 BeanPostProcessor#postProcessAfterInitialization bean初始化後置方法
          * 其中 InitDestroyAnnotationBeanPostProcessor 來執行 @PreDestroy 標註的方法
          */ 
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

到此我們已經分析完SpringBoot啓動過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章