上篇寫了探索SpringBoot-結合idea搭建Maven工程 續(五),好了,基本上關於Idea
這塊暫時先告一個段落了。下面正式來探索下Spring Boot
相關的內容。java
在learn-spring-framework-2.x
中的pom文件中,加上最新的spring-context
依賴。表示在這個模塊中,咱們使用的依賴是spring2.x
。spring
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>2.0.8</version>
</dependency>
複製代碼
由於spring2.x
時,必須須要編寫xml
文件來加載spring上下文
。因此,咱們除了啓動類以後,還打算編寫三個xml
文件。bash
在configurable-context.xml
中,咱們使用import
和Java的System Property
來引入不一樣環境所須要的上下文。ide
<import resource="classpath:/META-INF/${env}-context.xml"/>
複製代碼
在dev-context.xml
和prod-context.xml
中分別引入相同名稱的Bean
,可是這個Bean
的存在不一樣的屬性值。函數
dev-context.xml
post
<!-- dev 環境 value Bean 定義-->
<bean id="name" class="java.lang.String">
<constructor-arg>
<value>shane</value>
</constructor-arg>
</bean>
複製代碼
prod-context.xml
學習
<!-- prod 環境 name Bean 定義-->
<bean id="name" class="java.lang.String">
<constructor-arg>
<value>微秒</value>
</constructor-arg>
</bean>
複製代碼
最後定義啓動類,啓動類顯示加載Spring
上下文,並輸出id
爲name
的Bean
的屬性值。固然會根據System Property
的內容來動態加載不一樣環境下的Bean
,而且輸出不一樣的值。this
這麼作,也是爲了演示Spring
最最基礎的功能,做爲一個對象工廠的能力。idea
ConfigurableApplicationContextBootstrap.java
spa
public class ConfigurableApplicationContextBootstrap {
static {
// 調整系統屬性 "env",實現 "name" bean 的定義切換
// envValue 可能來自於 "-D" 命令行啓動參數
// 參數當不存在時,使用 "prod" 做爲默認值
String envValue = System.getProperty("env", "dev");
System.setProperty("env", envValue);
}
public static void main(String[] args) {
// 定義 XML ApplicationContext
// 先留意下這個location的方式,不須要寫classpath的前綴
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/configurable-context.xml");
// "name" bean 對象
String value = (String) context.getBean("name");
// "name" bean 內容輸出
System.out.println("Bean 'name' 的內容爲:" + value);
// 關閉上下文
context.close();
}
複製代碼
控制檯輸出
思考下Spring
在這個過程當中,作了什麼事情呢?
xml
文件,初始化Spring上下文
Spring上下文
中的對象咱們進入到ClassPathXmlApplicationContext(String)
構造函數中,能夠發現調用了另一個構造函數ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
並且設置了refresh
參數爲true
,parent
參數爲null
。
/** * Create a new ClassPathXmlApplicationContext, loading the definitions * from the given XML file and automatically refreshing the context. * @param configLocation resource location * @throws BeansException if context creation failed */
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
/** * Create a new ClassPathXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * @param configLocations array of resource locations * @param refresh whether to automatically refresh the context, * loading all bean definitions and creating all singletons. * Alternatively, call refresh manually after further configuring the context. * @param parent the parent context * @throws BeansException if context creation failed * @see #refresh() */
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
複製代碼
經過註釋能夠知道構造函數的做用是用給定的parent建立一個ClassPathXmlApplicationContext,而且根據XML文件加載定義的對象
bean
定義而且建立全部的單例對象Spring
是存在父子上下文的,以後有機會講到)構造函數首先解析了資源文件並設置爲上下文的一個屬性,以後進入到了關鍵的refresh
函數中。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing. [1.爲refresh準備上下文]
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.[2.告知子類refresh內部bean工廠]
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.[3.準備須要在本次上文中使用的bean工廠]
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.[4.容許上下文子類的bean工廠調用初始化函數]
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.[5.調用在上下文的註冊的工廠處理器]
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.[6.註冊在bean建立的過程當中的攔截處理器]
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.[7.初始化消息源]
initMessageSource();
// Initialize event multicaster for this context.[8.初始化事件多播機制]
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.[9.在這個特定的上下文子類中初始化其餘特殊的beans]
onRefresh();
// Check for listener beans and register them.[10.檢查監聽器的beans而且註冊他們]
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.[11.初始化存在的單例,不包括懶加載的對象]
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.[12.最後一步:發佈相關的事件]
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
beanFactory.destroySingletons();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
複製代碼
看個人中文註釋
,總共有12步核心步驟來初始化ClassPathXmlApplicationContext
。下面咱們在一步步來分析。
首先看prepareRefresh()
函數。
/** * Prepare this context for refreshing, setting its startup date and * active flag. */
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
synchronized (this.activeMonitor) {
this.active = true;
}
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
}
複製代碼
能夠看到就只有兩步核心操做。首先記錄了當前的時間,而後嘗試獲取activeMonitor
的鎖。能夠activeMonitor
的做用後面聯繫起來再分析。
下一篇分析obtainFreshBeanFactory()
函數,一步步來,畢竟是探索系列
嘛,不知道的內容,不斷地探索
,咱們才能將其轉換爲咱們知道的東西,這就是學習。
固然這是Spring
部分的核心的源代碼,不過由於SpringBoot
實際上是構建在Spring
基礎之上的,因此Spring
的部分源代碼也會有講解。
之後這裏天天都會寫一篇文章,題材不限,內容不限,字數不限。儘可能把本身天天的思考都放入其中。
若是這篇文章給你帶來了幫助,能請你寫下是哪一個部分嗎?有效的反饋是對我最大的幫助。
我是shane。今天是2019年8月11日。百天寫做計劃的第十八天,18/100。