淺析Spring Framework框架容器啓動過程

構建Spring環境

採用ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");方式構建Spring容器並查看其內部運行過程.php

Spring 版本 5.1.3.RELEASE

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
複製代碼

測試類

public class User {
    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
複製代碼

Spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="user" class="com.jimisun.learnspringboot.web.User">
        <constructor-arg index="0" value="jimisun"/>
        <constructor-arg index="1" value="jimisun"/>
    </bean>

</beans>
複製代碼

測試方法Main

public class Main {
        public static void main(String[] args) {
            // 用咱們的配置文件來啓動一個 ApplicationContext
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
            System.out.println("context 啓動成功");
            User user = context.getBean(User.class);
            System.out.println(user.toString());
        }
}
複製代碼

快速進入Debug查看IOC容器構建源碼

ApplicationContext 啓動過程當中,會建立SPring Bean容器,而後初始化相關Bean,再向Bean中注入其相關依賴。html

因此咱們僅僅須要Debug跟蹤Main方法中ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");這一句代碼,查看Spring是如何建立ApplicationContext容器並將xml中的配置信息裝配進容器的.java

Spring IOC源碼步驟分析

第一步: 檢查並設置Spring XML配置文件

功能:設置此應用程序上下文的配置文件位置,若是未設置;Spring能夠根據須要使用默認值web

setConfigLocations(configLocations);spring

在Main方法Debug啓動進入斷點,按F7跟進入其方法查看,會進入ClassPathXmlApplicationContext類的構造方法中數組

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
  private Resource[] configResources;
  // 若是已經有 ApplicationContext 並須要配置成父子關係,那麼調用這個構造方法
  public ClassPathXmlApplicationContext(ApplicationContext parent) {
    super(parent);
  }
  ...
  public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {

    super(parent);
    // 根據提供的路徑,處理成配置文件數組(以分號、逗號、空格、tab、換行符分割)
    setConfigLocations(configLocations);
    if (refresh) {
      refresh(); // 核心方法 剩餘的全部步驟都在此方法中!!!
    }
  }
    ...
}

複製代碼

首先執行"設置配置位置setConfigLocations"的方法,解析SpringXML配置文件地址存儲到configLocations屬性中。緩存

public void setConfigLocations(@Nullable String... locations) {
        //判斷配置路徑是否爲null
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
            //循環將配置文件路徑存儲到屬性configLocations中
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}
複製代碼

第二步:執行建立Bean容器以前的準備工做

注意:除了第一步設置XML配置文件路徑,剩餘的步驟都在該類的refresh();這個方法中執行,因此咱們須要Debug跟進入這個方法springboot

refresh();方法以下所示;由於整個SpringApplication的構建都在這個方法 裏面因此就如今這裏展示一下和你們混個臉熟.架構

public void refresh() throws BeansException, IllegalStateException {
    &emsp;//對下面的代碼塊添加同步鎖
   synchronized (this.startupShutdownMonitor) {
   &emsp;//第二步: 執行建立容器前的準備工做 :記錄下容器的啓動時間、標記「已啓動」狀態、處理配置文件中的佔位符
      prepareRefresh();
&emsp;&emsp;//第三步:建立Bean容器,加載XML配置信息 : 若是存在容器進行銷燬舊容器,建立新容器,解析XML配置文件爲一個個BeanDefinition定義註冊到新容器(BeanFactory)中,注意Bean未初始化 
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      //第四步: 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動註冊幾個特殊的 bean
      prepareBeanFactory(beanFactory);
      try {
         //第五步:加載並執行後置處理器 
         postProcessBeanFactory(beanFactory);
         //執行postProcessBeanFactory()方法
         invokeBeanFactoryPostProcessors(beanFactory);
          // 實例化攔截Bean建立的後置處理器beanPostProcessors
         registerBeanPostProcessors(beanFactory);
         //第六步: 初始化Spring容器的消息源
         initMessageSource();
         //第七步:初始化Spring容器事件廣播器
         initApplicationEventMulticaster();
         // 空方法
         onRefresh();
         //第八步:註冊事件監聽器&emsp;
         registerListeners();
         //第九步核心方法:初始化(構造)全部在XML文件中配置的單例非延遲加載的bean
         finishBeanFactoryInitialization(beanFactory);
         //第十步:清理緩存,若是容器中存Bean名爲lifecycleProcessor的Bean 對其進行註冊,若是不存在建立一個DefaultLifecycleProcessor進行註冊
         finishRefresh();
      }
      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }
         // 摧毀已經建立的單身人士以免懸空資源。
         destroyBeans();
         // 重置'有效'標誌。
         cancelRefresh(ex);
         // 向調用者傳播異常。
         throw ex;
      }

      finally {
         //重置Spring核心的工具類的緩存
         resetCommonCaches();
      }
   }
}
複製代碼

第二步的主要工做:準備工做,記錄下容器的啓動時間、標記「已啓動」狀態、處理配置文件中的佔位符,初始化事件屬性。app

prepareRefresh();

protected void prepareRefresh() {
   // 記錄啓動時間,
   // 將 active 屬性設置爲 true,closed 屬性設置爲 false,它們都是 AtomicBoolean類型
   this.startupDate = System.currentTimeMillis();
   this.closed.set(false);
   this.active.set(true);
	//打印Logger
   	if (logger.isDebugEnabled()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Refreshing " + this);
			}
			else {
				logger.debug("Refreshing " + getDisplayName());
			}
	}
	
   // 在上下文環境中初始化任何佔位符屬性源 空方法 默認狀況下不執行任何操做。
   initPropertySources();
   // 校驗 xml 配置文件
   getEnvironment().validateRequiredProperties();
   this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
複製代碼

第三步:建立 Bean 容器,加載並註冊 Bean

主要工做:進行銷燬舊容器,建立新容器,加載BeanDefinition到BeanFactory

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); ​

@Override
	protected final void refreshBeanFactory() throws BeansException {
	      // 若是ApplicationContext中已經的BeanFactory屬性已經有值,銷燬此BeanFactory全部 Bean,關閉 BeanFactory,從新建立一個新的Bean容器設置給ApplicationContext的beanFactory屬性
		if (hasBeanFactory()) {
		    //銷燬容器
			destroyBeans();
			//建立類型爲DefaultListableBeanFactory新容器放入BeanFactory變量中
			closeBeanFactory();
		}
		try {
		    //建立類型爲DefaultListableBeanFactory新容器放入BeanFactory變量中
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			//設置BeanFactory的序列化ID也就是其類名
			beanFactory.setSerializationId(getId());
			// 設置 BeanFactory 的兩個配置屬性:是否容許 Bean 覆蓋、是否容許循環引用
			customizeBeanFactory(beanFactory);
			//這個方法將根據配置,加載各個Bean,而後放到 BeanFactory 中&emsp;注意:這裏的加載並非初始化這個Bean 而是以Key-value的形式存儲在beanFactory; beanName-> beanDefinition 的 map
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
複製代碼

第四步:配置 Bean容器: prepareBeanFactory

主要工做:在Bean容器建立完畢會"手動"註冊一些特殊的 bean。官網這樣解釋: " 配置工廠的標準上下文特徵,例如上下文的ClassLoader和後處理器 "。

具體方法 : prepareBeanFactory(factory) ;

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   		// 這裏設置爲加載當前 ApplicationContext 類的類加載器
		beanFactory.setBeanClassLoader(getClassLoader());
		// 設置 Bean的表達式解析器
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    //默認添加一個ApplicationContextAwareProcessor的BeanPostProcessor,實現了ApplicationContextAware接口的Bean,Spring會將上下文ApplicationContext注入Bean屬性中
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		   // 下面幾行的意思就是,若是某個 bean 依賴於如下幾個接口的實現類,在自動裝配的時候忽略它們,
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
 /** * 下面幾行就是爲特殊的幾個 bean 賦值,若是有 bean 依賴瞭如下幾個,會注入這邊相應的值, * 以前咱們說過,"當前 ApplicationContext 持有一個 BeanFactory",這裏解釋了第一行 * ApplicationContext 還繼承了 ResourceLoader、ApplicationEventPublisher、MessageSource * 因此對於這幾個依賴,能夠賦值爲 this,注意 this 是一個 ApplicationContext * 那這裏怎麼沒看到爲 MessageSource 賦值呢?那是由於 MessageSource 被註冊成爲了一個普通的 bean */	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
		//註冊早期後處理器以檢測內部bean做爲ApplicationListeners
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
	// 若是檢測到LoadTimeWeaver 準備編織 不是咱們本章的重點無需關注
	if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}
	//默認註冊 environment systemEnvironment systemProperties的Bean 咱們能夠選擇覆蓋
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}
}
```
	
複製代碼

第五步: 處理自定義Bean的後置處理器

主要功能:實例化在XML配置中實現了BeanFactoryPostProcessor和BeanPostProcessors接口的Bean並執行其回調方法.注意:此時普通的Bean仍然並無初始化

//實例化並調用XML配置中實現了BeanFactoryPostProcessors接口的的回調
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
//實例化並調用XML配置中實現了BeanPostProcessors接口的的回調
registerBeanPostProcessors(beanFactory);
複製代碼
//註解:這裏在建立完成Bean容器後執行BeanFactoryPostProcessors接口的回調,咱們能夠在Bean容器初始化完成的時候完成咱們本身的業務邏輯(不多用),而後是registerBeanPostProcessors(beanFactory)方法,此方法的官方解釋是:"Register bean processors that intercept bean creation(若是存在則註冊攔截bean建立的bean後置處理器)"
複製代碼

第六步: 初始化Spring容器的消息源

主要功能: 初始化MessageSource。若是在此上下文中未定義,則使用parent。

// 初始化ApplicationContext的消息源。
initMessageSource();
複製代碼
protected void initMessageSource() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //判斷beanFactory中是否有messageSource的Bean
		if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
			this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
			// 使MessageSource知道父MessageSource
			if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
				HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
				if (hms.getParentMessageSource() == null) {
				//若是沒有父MessageSource,則此消息源設置爲父MessageSource
					hms.setParentMessageSource(getInternalParentMessageSource());
				}
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Using MessageSource [" + this.messageSource + "]");
			}
		}
		else {
			// 若是沒有則建立一個默認的DelegatingMessageSource消息源
			DelegatingMessageSource dms = new DelegatingMessageSource();
			dms.setParentMessageSource(getInternalParentMessageSource());
			this.messageSource = dms;
			beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
			}
		}
	}
複製代碼

第七步: 初始化Spring容器事件廣播器

PS : 在實際項目中咱們不多會用到Spring的事件廣播器,由於如今都是分佈式應用了局部通信不多使用了 一篇很棒的關於Spring容器的事件講解 juejin.im/post/5a543c…

主要功能 : 註冊Spring的事件廣播器用於廣播Spring的內置事件和自定義事件

initApplicationEventMulticaster();

protected void initApplicationEventMulticaster() {
	//初始化ApplicationEventMulticaster
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
    //若是在上下文中沒有定義,則建立一個默認的SimpleApplicationEventMulticaster。
		else {
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}
複製代碼

第八步:註冊事件監聽器

主要功能 : 實例化實現ApplicationListener接口的bean。

// 註冊監聽器
finishBeanFactoryInitialization(beanFactory);
複製代碼
protected void registerListeners() {
		//首先註冊靜態指定的偵聽器
		for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}
        //下面是咱們自定義的監聽器,Spring文檔中給出的建議是 "不要在這裏初始化FactoryBeans:咱們須要保留全部常規bean"
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		for (String listenerBeanName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}
        //使用已經註冊的事件廣播器,發佈早期的應用程序事件......
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (earlyEventsToProcess != null) {
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}
複製代碼

第九步: 實例化全部的單例Bean

功能:執行到這一步,Spring.xml配置文件中的特殊的Bean該註冊的也註冊了,該調用的也調用了,就剩下了普通的Bean了,在這一步就都實例化了.(僅僅是非延遲實例化的單例Bean),也就是說這一步就已經完成了Bean工廠(ApplicationContext)的初始化了.

// 實例化全部SPring.xml配置文件中配置的非延遲實例化的單例Bean
finishBeanFactoryInitialization(beanFactory);
複製代碼
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// 初始化此上下文的轉換服務
		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));
		}

		//若是沒有bean後處理器,則註冊默認的嵌入值解析器(例如PropertyPlaceholderConfigurer bean)以前註冊過;此時,主要用於註釋屬性值的分辨率。
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// 儘早初始化LoadTimeWeaverAware bean以容許儘早註冊其變換器。
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}
		// 中止使用臨時ClassLoader進行類型匹配。
		beanFactory.setTempClassLoader(null);
		// 容許緩存全部bean定義元數據,而不指望進一步的更改。
		beanFactory.freezeConfiguration();
		// 實例化全部剩餘(非延遲初始化)單例。
		beanFactory.preInstantiateSingletons();
	}
複製代碼

第十步:完成ApplicationContext容器的初始化收

功能:進行相關的容器建立完成時的操做,回收相關資源

finishRefresh();
 resetCommonCaches();
複製代碼
protected void finishRefresh() {
		//清除上下文級資源緩存(例如來自掃描的ASM元數據)。
		clearResourceCaches();
		//爲此上下文初始化生命週期處理器。
		initLifecycleProcessor();
		// 首先將刷新傳播到生命週期處理器。
		getLifecycleProcessor().onRefresh();
		// 廣播最終事件
		publishEvent(new ContextRefreshedEvent(this));
		// 若是處於活動狀態,請參與LiveBeansView 
		LiveBeansView.registerApplicationContext(this);
	}
複製代碼
//清除一些單例的工具類的緩存
protected void resetCommonCaches() {
		ReflectionUtils.clearCache();
		AnnotationUtils.clearCache();
		ResolvableType.clearCache();
CachedIntrospectionResults.clearClassLoader(getClassLoader());
	}
複製代碼

應該學習到的知識

  • Spring容器中管理的Bean是Class嗎?

能夠看到Bean容器中的Bean定義映射關係的Map中存放的是key(String)->GenericBeanDefinition的映射,那麼GenericBeanDefinition又是什麼呢?

BeanDefinition 中保存了咱們的 Bean 信息,好比這個 Bean 指向的是哪一個類、是不是單例的、是否懶加載、這個 Bean 依賴了哪些 Bean 等等。

  • SpringBean是被ApplicationContext管理的嗎?

經過Debug的過程當中咱們能夠看到咱們使用ClassPathXmlApplicationContext構造的ApplicationContext對象其實在內部維護了一個屬性名爲beanFactory,咱們的SpringBean都被定義在這個屬性裏面,也就是說beanFactory這個屬性纔是容器,ApplicationContext僅僅是作了一層包裝.那麼beanFactory又是什麼呢?

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
		...
}
複製代碼

3

能夠看到DefaultListableBeanFactory類也是Bean容器,並且是繼承了全部其餘的容器的功能,能夠說是最爲強大的容器;例如具備(分層,獲取多個容器,注入功能....)

本章小結

第一次參閱源碼寫的比較慎重,其中因爲身體抱恙又有所當誤,因此在發佈本章的時候也是幾天後了,總的來講本章並無什麼重點,僅僅是把Spring的IOC容器的啓動過程進行了標註,並未作過多底層的深度剖析,例如loadBeanDefinitions(beanFactory)Spring如何將XMl文件的配置裝載入Bean工廠,以及後面的每一個註釋均可以新開一篇長篇大論的文章,後面儘量的在Spring Framework深度剖析專欄中更爲詳細的學習Spring總體架構源碼,本文是根據原文https://juejin.im/post/5bc5c88df265da0b001f5dee的學習筆記,將步驟更爲清晰的展示

該教程所屬Java工程師之Spring Framework深度剖析專欄,本系列相關博文目錄 Java工程師之Spring Framework深度剖析專欄

相關文章
相關標籤/搜索