spring bean載入原理
阿新 • • 發佈:2019-01-03
簡單的分析了一下spring bean的載入原理,屬於個人的理解,原始碼比這個要複雜的多:
spring的配置檔案applicationContext.xml的內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!-- 使用者dao類,就是一個普通的pojo,裡面有個addUser方法,呼叫會輸出一行字 -->
<bean id="userDao" class="com.qjl.study.spring.dao.impl.UserDaoImpl"></bean>
</beans >
非web環境下我們通常這麼來載入IOC容器,獲取bean:
BeanFactory ac = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserDao userDao = ac.getBean("userDao");
所以,我簡單的實現了一下這個BeanFactory介面和ClassPathXmlApplicationContext類(不過一般上是用ApplicationContext這個介面來接收ClassPathXmlApplicationContext,不過就本例來說沒有太大的區別)。
BeanFactory介面的程式碼如下:
package com.qjl.study.spring.factory;
/**
* 類名稱: bean工廠
* 類描述: 例項化各種bean
* 全限定性類名: com.qjl.study.spring.factory.BeanFactory
* @author MrQJL
* @date 2018年1月3日 下午9:46:30
* @version V1.0
*/
public interface BeanFactory {
/**
* 通過bean的id獲取例項化的bean物件
* 獲取bean的時候,該bean可能不存在,所以要丟擲異常
* @param bean的名稱
* @return 例項化的bean物件
*/
Object getBean(String name) throws Exception;
}
我在BeanFactory裡面就寫了一個getBean(String name)方法,用於輸入bean的id,返回對應的例項,因為輸入的bean的id可能不存在,所以要丟擲異常。
ClassPathXmlApplicationContext類實現了BeanFactory介面,程式碼如下:
package com.qjl.study.spring.factory.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import com.qjl.study.spring.factory.BeanFactory;
/**
* 類名稱: 上下文物件
* 類描述: 用於獲取編譯路徑下xml檔案所對應的bean的IOC容器
* 全限定性類名: com.qjl.study.spring.factory.impl.ClassPathXmlApplicationContext
* @author MrQJL
* @date 2018年1月3日 下午10:04:50
* @version V1.0
*/
public class ClassPathXmlApplicationContext implements BeanFactory {
/**
* 這就是那個IOC容器
*/
private Map<String, Object> beans = new HashMap<String, Object>();
public ClassPathXmlApplicationContext(String configLocation) throws Exception {
// 使用jdom的SAXBuilder讀取xml檔案
SAXBuilder sb = new SAXBuilder();
// 載入xml文件進記憶體
Document doc = sb.build(this.getClass().getClassLoader()
.getResourceAsStream(configLocation));
// 獲取根節點--也就是beans
Element root = doc.getRootElement();
// 獲取根節點的孩子節點--也就是bean
@SuppressWarnings("unchecked")
List<Object> childList = root.getChildren("bean");
// 迴圈取出每一個bean節點以及他們的id和class屬性,利用反射建立一個物件
for (int i = 0; i < childList.size(); i++) {
Element child = (Element) childList.get(i);
// 獲取id屬性
String id = child.getAttributeValue("id");
// 獲取class屬性
String clazz = child.getAttributeValue("class");
// 通過反射載入類,例項化bean物件
Object obj = Class.forName(clazz).newInstance();
// 將例項化的物件放入IOC容器(map)中
beans.put(id, obj);
}
}
@Override
public Object getBean(String name) {
return beans.get(name);
}
}
下面就從這個ClassPathXmlApplicationContext的建構函式開始說起:
1.讀取spring的配置檔案
呼叫ClassPathXmlApplicationContext的有參構造方法,傳入配置檔案的名稱。
呼叫SAXBuilder的build方法讀取配置檔案
// 使用jdom的SAXBuilder讀取xml檔案,也可以使用dom4j,SAX讀取xml配置檔案
SAXBuilder sb = new SAXBuilder();
// 載入xml文件進記憶體,configLocation就是傳入的檔名,Document是jdom包裡面的類
Document doc = sb.build(this.getClass().getClassLoader().getResourceAsStream(configLocation));
// 獲取根節點,也就是beans
Element root = doc.getRootElement();
// 獲取根節點的孩子節點,也就是bean
List<Object> childList = root.getChildren("bean");
2.反射載入類,例項化物件,並將物件放入IOC容器
// 迴圈取出每一個bean節點以及他們的id和class屬性,利用反射建立一個物件
for (int i = 0; i < childList.size(); i++) {
Element child = (Element) childList.get(i);
// 獲取bean標籤的id屬性
String id = child.getAttributeValue("id");
// 獲取bean標籤的class屬性
String clazz = child.getAttributeValue("class");
// 通過反射載入類,例項化bean物件
Object obj = Class.forName(clazz).newInstance();
// 將例項化的物件放入IOC容器(map)中
beans.put(id, obj);
}
3.通過getBean從IOC容器獲取物件
@Override
public Object getBean(String name) {
return beans.get(name);
}
至此,建構函式內的業務邏輯執行完畢,配置檔案中配置的bean都載入並例項化完畢,呼叫getBean方法獲取對應的例項物件即可。
spring bean載入的大體流程就是這樣,理解了基本的原理後,再閱讀原始碼就會輕鬆一些了。